mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
307 lines
11 KiB
TypeScript
307 lines
11 KiB
TypeScript
import * as proxyquire from 'proxyquire';
|
|
import * as nsWebpackIndex from '../index';
|
|
import { join } from 'path';
|
|
// With noCallThru enabled, `proxyquire` will not fall back to requiring the real module to populate properties that are not mocked.
|
|
// This allows us to mock packages that are not available in node_modules.
|
|
// In case you want to enable fallback for a specific object, just add `'@noCallThru': false`.
|
|
proxyquire.noCallThru();
|
|
|
|
class EmptyClass {}
|
|
|
|
let angularCompilerOptions: any;
|
|
class AngularCompilerStub {
|
|
constructor(options) {
|
|
angularCompilerOptions = options;
|
|
}
|
|
}
|
|
|
|
let terserOptions: any;
|
|
class TerserJsStub {
|
|
constructor(options) {
|
|
terserOptions = options;
|
|
}
|
|
}
|
|
|
|
const nativeScriptDevWebpack = {
|
|
GenerateBundleStarterPlugin: EmptyClass,
|
|
GenerateNativeScriptEntryPointsPlugin: EmptyClass,
|
|
WatchStateLoggerPlugin: EmptyClass,
|
|
PlatformFSPlugin: EmptyClass,
|
|
getAppPath: () => 'app',
|
|
getEntryModule: () => 'EntryModule',
|
|
hasRootLevelScopedModules: () => false,
|
|
hasRootLevelScopedAngular: () => false,
|
|
processTsPathsForScopedModules: () => false,
|
|
processTsPathsForScopedAngular: () => false,
|
|
getResolver: () => null,
|
|
getConvertedExternals: nsWebpackIndex.getConvertedExternals,
|
|
getSourceMapFilename: nsWebpackIndex.getSourceMapFilename,
|
|
processAppComponents: nsWebpackIndex.processAppComponents,
|
|
getUserDefinedEntries: nsWebpackIndex.getUserDefinedEntries,
|
|
};
|
|
|
|
const emptyObject = {};
|
|
const FakeHmrTransformerFlag = 'hmr';
|
|
const FakeNativeClassTransformerFlag = 'NativeClass';
|
|
const FakeLazyTransformerFlag = 'lazy';
|
|
const webpackConfigAngular = proxyquire('./webpack.angular', {
|
|
'@nativescript/webpack': nativeScriptDevWebpack,
|
|
'@nativescript/webpack/nativescript-target': emptyObject,
|
|
'@nativescript/webpack/transformers/ns-support-hmr-ng': {
|
|
nsSupportHmrNg: () => {
|
|
return FakeHmrTransformerFlag;
|
|
},
|
|
},
|
|
'@nativescript/webpack/transformers/ns-transform-native-classes-ng': {
|
|
nsTransformNativeClassesNg: () => {
|
|
return FakeNativeClassTransformerFlag;
|
|
},
|
|
},
|
|
'@nativescript/webpack/helpers/angular-config-parser': {
|
|
parseWorkspaceConfig: (platform, envConfigs, rootPath = '') => {
|
|
return {
|
|
fileReplacements: {},
|
|
copyReplacements: [],
|
|
};
|
|
},
|
|
hasConfigurations: (envConfigs) => {
|
|
return false;
|
|
},
|
|
},
|
|
'@nativescript/webpack/utils/ast-utils': {
|
|
getMainModulePath: () => {
|
|
return 'fakePath';
|
|
},
|
|
},
|
|
'@nativescript/webpack/utils/tsconfig-utils': {
|
|
getNoEmitOnErrorFromTSConfig: () => {
|
|
return false;
|
|
},
|
|
getCompilerOptionsFromTSConfig: () => {
|
|
return false;
|
|
},
|
|
},
|
|
'@nativescript/webpack/plugins/NativeScriptAngularCompilerPlugin': {
|
|
getAngularCompilerPlugin: () => {
|
|
return AngularCompilerStub;
|
|
},
|
|
},
|
|
'@ngtools/webpack': {
|
|
AngularCompilerPlugin: AngularCompilerStub,
|
|
},
|
|
'terser-webpack-plugin': TerserJsStub,
|
|
});
|
|
|
|
const webpackConfigTypeScript = proxyquire('./webpack.typescript', {
|
|
'@nativescript/webpack': nativeScriptDevWebpack,
|
|
'@nativescript/webpack/nativescript-target': emptyObject,
|
|
'@nativescript/webpack/transformers/ns-transform-native-classes': emptyObject,
|
|
'@nativescript/webpack/utils/tsconfig-utils': {
|
|
getNoEmitOnErrorFromTSConfig: () => {
|
|
return false;
|
|
},
|
|
getCompilerOptionsFromTSConfig: () => {
|
|
return false;
|
|
},
|
|
},
|
|
'terser-webpack-plugin': TerserJsStub,
|
|
});
|
|
|
|
const webpackConfigJavaScript = proxyquire('./webpack.javascript', {
|
|
'@nativescript/webpack': nativeScriptDevWebpack,
|
|
'@nativescript/webpack/nativescript-target': emptyObject,
|
|
'terser-webpack-plugin': TerserJsStub,
|
|
});
|
|
|
|
const webpackConfigVue = proxyquire('./webpack.vue', {
|
|
'@nativescript/webpack': nativeScriptDevWebpack,
|
|
'@nativescript/webpack/nativescript-target': emptyObject,
|
|
'@nativescript/webpack/transformers/ns-transform-native-classes': emptyObject,
|
|
'vue-loader/lib/plugin': EmptyClass,
|
|
'nativescript-vue-template-compiler': emptyObject,
|
|
'terser-webpack-plugin': TerserJsStub,
|
|
});
|
|
|
|
describe('webpack.config.js', () => {
|
|
const getInput = (options: { platform: string; hmr?: boolean; externals?: string[]; sourceMap?: boolean; hiddenSourceMap?: boolean | string }) => {
|
|
const input: any = Object.assign({}, options);
|
|
input[options.platform] = true;
|
|
return input;
|
|
};
|
|
|
|
[
|
|
{ type: 'javascript', webpackConfig: webpackConfigJavaScript },
|
|
{ type: 'typescript', webpackConfig: webpackConfigTypeScript },
|
|
{ type: 'angular', webpackConfig: webpackConfigAngular },
|
|
{ type: 'vue', webpackConfig: webpackConfigVue },
|
|
].forEach((element) => {
|
|
const { type, webpackConfig } = element;
|
|
['android', 'ios'].forEach((platform) => {
|
|
describe(`verify externals for webpack.${type}.js (${platform})`, () => {
|
|
afterEach(() => {
|
|
nativeScriptDevWebpack.getConvertedExternals = nsWebpackIndex.getConvertedExternals;
|
|
});
|
|
|
|
it('returns empty array when externals are not passed', () => {
|
|
const input = getInput({ platform });
|
|
const config = webpackConfig(input);
|
|
expect(config.externals).toEqual([]);
|
|
});
|
|
|
|
it('calls getConvertedExternals to convert externals', () => {
|
|
let isCalled = false;
|
|
nativeScriptDevWebpack.getConvertedExternals = () => {
|
|
isCalled = true;
|
|
return [];
|
|
};
|
|
|
|
const input = getInput({ platform, externals: ['nativescript-vue'] });
|
|
webpackConfig(input);
|
|
expect(isCalled).toBe(true, 'Webpack.config.js must use the getConvertedExternals method');
|
|
});
|
|
|
|
// if (platform === "ios") {
|
|
// it('has inspector_modules entry when tns-core-modules are not externals', () => {
|
|
// const input = getInput({ platform, externals: ['nativescript-vue'] });
|
|
// const config = webpackConfig(input);
|
|
// expect(config.entry["tns_modules/@nativescript/core/inspector_modules"]).toEqual("inspector_modules");
|
|
// });
|
|
|
|
// it('does not have inspector_modules entry when @nativescript/core are externals', () => {
|
|
// const input = getInput({ platform, externals: ['@nativescript'] });
|
|
// const config = webpackConfig(input);
|
|
// expect(config.entry["tns_modules/@nativescript/core/inspector_modules"]).toBeUndefined();
|
|
// });
|
|
// }
|
|
|
|
[
|
|
{
|
|
input: ['nativescript-vue'],
|
|
expectedOutput: [/^nativescript-vue((\/.*)|$)/],
|
|
},
|
|
{
|
|
input: ['nativescript-vue', 'nativescript-angular'],
|
|
expectedOutput: [/^nativescript-vue((\/.*)|$)/, /^nativescript-angular((\/.*)|$)/],
|
|
},
|
|
].forEach((testCase) => {
|
|
const input = getInput({ platform, externals: testCase.input });
|
|
|
|
it(`are correct regular expressions, for input ${testCase.input}`, () => {
|
|
const config = webpackConfig(input);
|
|
expect(config.externals).toEqual(testCase.expectedOutput);
|
|
});
|
|
});
|
|
});
|
|
|
|
if (type === 'angular') {
|
|
describe(`angular transformers for webpack.${type}.js (${platform})`, () => {
|
|
beforeEach(() => {
|
|
angularCompilerOptions = null;
|
|
});
|
|
|
|
it('should contain only NativeClass transformer by default', () => {
|
|
const input = getInput({ platform });
|
|
|
|
webpackConfig(input);
|
|
|
|
expect(angularCompilerOptions).toBeDefined();
|
|
expect(angularCompilerOptions.platformTransformers).toBeDefined();
|
|
expect(angularCompilerOptions.platformTransformers.length).toEqual(1);
|
|
expect(angularCompilerOptions.platformTransformers[0]).toEqual(FakeNativeClassTransformerFlag);
|
|
});
|
|
|
|
it('should contain the HMR transformer when the HMR flag is passed', () => {
|
|
const input = getInput({ platform, hmr: true });
|
|
|
|
webpackConfig(input);
|
|
|
|
expect(angularCompilerOptions).toBeDefined();
|
|
expect(angularCompilerOptions.platformTransformers).toBeDefined();
|
|
expect(angularCompilerOptions.platformTransformers.length).toEqual(2);
|
|
expect(angularCompilerOptions.platformTransformers[1]).toEqual(FakeHmrTransformerFlag);
|
|
});
|
|
});
|
|
}
|
|
|
|
describe(`source map for webpack.${type}.js (${platform})`, () => {
|
|
beforeEach(() => {
|
|
terserOptions = null;
|
|
});
|
|
|
|
it('should not set source maps without the flag', () => {
|
|
const input = getInput({ platform, sourceMap: false });
|
|
|
|
const config = webpackConfig(input);
|
|
|
|
expect(config.devtool).toEqual('none');
|
|
expect(terserOptions.sourceMap).toBeFalsy();
|
|
expect(terserOptions.terserOptions.output.semicolons).toBeTruthy();
|
|
expect(config.output.sourceMapFilename).toEqual('[file].map');
|
|
});
|
|
|
|
it('should set inline-source-map devtool', () => {
|
|
const input = getInput({ platform, sourceMap: true });
|
|
|
|
const config = webpackConfig(input);
|
|
|
|
expect(config.devtool).toEqual('inline-source-map');
|
|
expect(terserOptions.sourceMap).toBeTruthy();
|
|
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
|
|
expect(config.output.sourceMapFilename).toEqual('[file].map');
|
|
});
|
|
});
|
|
|
|
describe(`hidden source map for webpack.${type}.js (${platform})`, () => {
|
|
beforeEach(() => {
|
|
terserOptions = null;
|
|
});
|
|
|
|
it('should not set source maps without the flag', () => {
|
|
const input = getInput({ platform, hiddenSourceMap: false });
|
|
|
|
const config = webpackConfig(input);
|
|
|
|
expect(config.devtool).toEqual('none');
|
|
expect(terserOptions.sourceMap).toBeFalsy();
|
|
expect(terserOptions.terserOptions.output.semicolons).toBeTruthy();
|
|
expect(config.output.sourceMapFilename).toEqual('[file].map');
|
|
});
|
|
|
|
it('should set hidden-source-map devtool and the default sourceMap folder', () => {
|
|
const input = getInput({ platform, hiddenSourceMap: true });
|
|
|
|
const config = webpackConfig(input);
|
|
|
|
expect(config.devtool).toEqual('hidden-source-map');
|
|
expect(terserOptions.sourceMap).toBeTruthy();
|
|
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
|
|
expect(config.output.sourceMapFilename).toEqual(join('..', 'sourceMap', '[file].map'));
|
|
});
|
|
|
|
it('should override the sourceMap property and the default sourceMap folder', () => {
|
|
const input = getInput({ platform, sourceMap: true, hiddenSourceMap: true });
|
|
|
|
const config = webpackConfig(input);
|
|
|
|
expect(config.devtool).toEqual('hidden-source-map');
|
|
expect(terserOptions.sourceMap).toBeTruthy();
|
|
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
|
|
expect(config.output.sourceMapFilename).toEqual(join('..', 'sourceMap', '[file].map'));
|
|
});
|
|
|
|
it('should set hidden-source-map devtool and override the sourceMapFilename', () => {
|
|
const newSourceMapFolder = 'myCoolSourceMapFolder';
|
|
const input = getInput({ platform, sourceMap: true, hiddenSourceMap: newSourceMapFolder });
|
|
|
|
const config = webpackConfig(input);
|
|
|
|
expect(config.devtool).toEqual('hidden-source-map');
|
|
expect(terserOptions.sourceMap).toBeTruthy();
|
|
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
|
|
expect(config.output.sourceMapFilename).toEqual(join('..', newSourceMapFolder, '[file].map'));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|