mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
228 lines
5.2 KiB
TypeScript
228 lines
5.2 KiB
TypeScript
// Make sure the Acorn Parser (used by Webpack) can parse ES-Stage3 code
|
|
// This must be at the top BEFORE webpack is loaded so that we can extend
|
|
// and replace the parser before webpack uses it
|
|
// Based on the issue: https://github.com/webpack/webpack/issues/10216
|
|
import stage3 from 'acorn-stage3';
|
|
|
|
// we use require to be able to override the exports
|
|
const acorn = require('acorn');
|
|
acorn.Parser = acorn.Parser.extend(stage3);
|
|
|
|
import { highlight } from 'cli-highlight';
|
|
import { merge } from 'webpack-merge';
|
|
import Config from 'webpack-chain';
|
|
import webpack from 'webpack';
|
|
|
|
import { applyExternalConfigs } from './helpers/externalConfigs';
|
|
import { determineProjectFlavor } from './helpers/flavor';
|
|
import { error, info } from './helpers/log';
|
|
import { configs } from './configuration';
|
|
import helpers from './helpers';
|
|
|
|
export interface IWebpackEnv {
|
|
[name: string]: any;
|
|
|
|
env?: string;
|
|
|
|
appPath?: string;
|
|
appResourcesPath?: string;
|
|
appComponents?: string[];
|
|
|
|
nativescriptLibPath?: string;
|
|
|
|
android?: boolean;
|
|
ios?: boolean;
|
|
// for custom platforms
|
|
platform?: string;
|
|
|
|
sourceMap?: string | boolean;
|
|
production?: boolean;
|
|
report?: boolean;
|
|
hmr?: boolean;
|
|
|
|
// enable verbose output
|
|
verbose?: boolean;
|
|
|
|
// misc
|
|
replace?: string[] | string;
|
|
}
|
|
|
|
interface IChainEntry {
|
|
chainFn: any;
|
|
order?: number;
|
|
plugin?: string;
|
|
}
|
|
|
|
let webpackChains: IChainEntry[] = [];
|
|
let webpackMerges: any[] = [];
|
|
let explicitUseConfig = false;
|
|
let hasInitialized = false;
|
|
let currentPlugin: string | undefined;
|
|
/**
|
|
* @internal
|
|
*/
|
|
export let env: IWebpackEnv = {};
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export function setCurrentPlugin(plugin: string) {
|
|
currentPlugin = plugin;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
export function clearCurrentPlugin() {
|
|
currentPlugin = undefined;
|
|
}
|
|
|
|
////// PUBLIC API
|
|
/**
|
|
* The default flavor specific configs
|
|
*/
|
|
export const defaultConfigs = configs;
|
|
|
|
/**
|
|
* Utilities to simplify various tasks
|
|
*/
|
|
export const Utils = helpers;
|
|
|
|
/**
|
|
* Initialize @nativescript/webpack with the webpack env.
|
|
* Must be called first.
|
|
*
|
|
* @param _env The webpack env
|
|
*/
|
|
export function init(_env: IWebpackEnv) {
|
|
hasInitialized = true;
|
|
if (_env) {
|
|
env = _env;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Explicitly specify the base config to use.
|
|
* Calling this will opt-out from automatic flavor detection.
|
|
*
|
|
* Useful when the flavor cannot be detected due to the project structure
|
|
* for example in a custom monorepo.
|
|
*
|
|
* @param config Name of the base config to use.
|
|
*/
|
|
export function useConfig(config: keyof typeof defaultConfigs | false) {
|
|
explicitUseConfig = true;
|
|
if (config) {
|
|
webpackChains.push({
|
|
order: -1,
|
|
chainFn: configs[config],
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a new function to be called when building the internal config using webpack-chain.
|
|
*
|
|
* @param chainFn A function that accepts the internal chain config, and the current environment
|
|
* @param options Optional options to control the order in which the chain function should be applied.
|
|
*/
|
|
export function chainWebpack(
|
|
chainFn: (config: Config, env: IWebpackEnv) => any,
|
|
options?: { order?: number }
|
|
) {
|
|
webpackChains.push({
|
|
order: options?.order || 0,
|
|
chainFn,
|
|
plugin: currentPlugin,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Merge an object into the resolved chain config.
|
|
*
|
|
* @param mergeFn An object or a function that optionally returns an object (can mutate the object directly and return nothing)
|
|
*/
|
|
export function mergeWebpack(
|
|
mergeFn: (
|
|
config: Partial<webpack.Configuration>,
|
|
env: IWebpackEnv
|
|
) => any | Partial<webpack.Configuration>
|
|
) {
|
|
webpackMerges.push(mergeFn);
|
|
}
|
|
|
|
/**
|
|
* Resolve a new instance of the internal chain config with all chain functions applied.
|
|
*/
|
|
export function resolveChainableConfig(): Config {
|
|
const config = new Config();
|
|
|
|
if (!explicitUseConfig) {
|
|
useConfig(determineProjectFlavor());
|
|
}
|
|
|
|
// apply configs from dependencies
|
|
// todo: allow opt-out
|
|
applyExternalConfigs();
|
|
|
|
webpackChains
|
|
.splice(0)
|
|
.sort((a, b) => {
|
|
return a.order - b.order;
|
|
})
|
|
.forEach(({ chainFn, plugin }) => {
|
|
try {
|
|
chainFn(config, env);
|
|
} catch (err) {
|
|
if (plugin) {
|
|
// catch and print errors from plugins
|
|
return error(`
|
|
Unable to apply chain function from: ${plugin}.
|
|
Error is: ${err}
|
|
`);
|
|
}
|
|
|
|
// otherwise throw - as the error is likely from the user config
|
|
// or missing env flags (eg. missing platform)
|
|
throw err;
|
|
}
|
|
});
|
|
|
|
if (env.verbose) {
|
|
info('Resolved chainable config (before merges):');
|
|
info(highlight(config.toString(), { language: 'js' }));
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
/**
|
|
* Resolve a "final" configuration that has all chain functions and merges applied.
|
|
*
|
|
* @param chainableConfig Optional chain config to use.
|
|
*/
|
|
export function resolveConfig(
|
|
chainableConfig = resolveChainableConfig()
|
|
): webpack.Configuration {
|
|
if (!hasInitialized) {
|
|
throw error('resolveConfig() must be called after init()');
|
|
}
|
|
|
|
let config = chainableConfig.toConfig();
|
|
|
|
// this applies webpack merges
|
|
webpackMerges.forEach((mergeFn) => {
|
|
if (typeof mergeFn === 'function') {
|
|
// mergeFn is a function with optional return value
|
|
const res = mergeFn(config, env);
|
|
if (res) config = merge(config, res);
|
|
} else if (mergeFn) {
|
|
// mergeFn is a literal value (object)
|
|
config = merge(config, mergeFn);
|
|
}
|
|
});
|
|
|
|
// return a config usable by webpack
|
|
return config;
|
|
}
|