Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

Commit 3362cd6

Browse files
fix: some packages are treated as externals when they shouldn't (#771)
* fix: some packages are treated as externals when they shouldn't In case `externals` are passed to webpack.config.js, they are converted to regular expression. The idea is to detect all imports for this module, for example if it is `nativescript-vue`, we would like to make externals imports like `nativescript-vue/somesubdir/file1`. However, the regular expression we construct also matches packages like `nativescript-vue-template`. Fix the regular expressions in all template webpack.config.js files and add unit tests for them. Add devDependency to `proxyquire` as the webpack configs that are tested, require some packages that are not available in node_modules of `nativescript-dev-webpack` (including the `nativescript-dev-webpack` itself). This is expected for the `webpack.config.js` files, as they should be placed in the NativeScript applications, so mocking the requires shouldn't be a problem in this case. * chore: move externals generation to a common place Move the conversion of regular expressions from `string[]` to `RegExp[]` to index.js file, so it can be reused in all webpack.config.js files. Move most of the tests to the `index.spec.js` due to this change. * chore: fix grammar errors Co-Authored-By: rosen-vladimirov <rosen-vladimirov@users.noreply.github.com>
1 parent c7bcbaf commit 3362cd6

File tree

8 files changed

+194
-15
lines changed

8 files changed

+194
-15
lines changed

‎index.js‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ exports.getAppPath = (platform, projectDir) => {
5353
}
5454
};
5555

56+
/**
57+
* Converts an array of strings externals to an array of regular expressions.
58+
* Input is an array of string, which we need to convert to regular expressions, so all imports for this module will be treated as externals.
59+
60+
* For example, in case we want nativescript-vue to be external, we will pass `["nativescript-vue"]`.
61+
* If we pass it to webpack in this way, it will treat all `require("nativescript-vue")` as externals.
62+
* However, you may import some file/subdir of the module, for example `require("nativescript-vue/somedir/file1")`.
63+
* To treat this as external, we convert the strings to regular expressions, which makes webpack exclude all imports of the module.
64+
* @param {string[]} externals Array of strings.
65+
* @returns {RegExp[]} Array of regular expressions constructed from the input strings. In case the input is nullable, an empty array is returned.
66+
*/
67+
exports.getConvertedExternals = (externals) => {
68+
const modifiedExternals = (externals || []).map((e) => {
69+
return new RegExp(`^${e}((/.*)|$)`);
70+
});
71+
72+
return modifiedExternals;
73+
};
74+
5675
const sanitize = name => name
5776
.split("")
5877
.filter(char => /[a-zA-Z0-9]/.test(char))

‎index.spec.ts‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { getConvertedExternals } from './index';
2+
3+
describe('index', () => {
4+
describe('getConvertedExternals', () => {
5+
it('returns empty array when nullable is passed', () => {
6+
const actualResult = getConvertedExternals(null);
7+
expect(actualResult).toEqual([]);
8+
});
9+
10+
const testCases = [
11+
{
12+
input: ['nativescript-vue'],
13+
expectedOutput: [/^nativescript-vue((\/.*)|$)/]
14+
},
15+
{
16+
input: ['nativescript-vue', 'nativescript-angular'],
17+
expectedOutput: [/^nativescript-vue((\/.*)|$)/, /^nativescript-angular((\/.*)|$)/]
18+
}
19+
];
20+
21+
testCases.forEach(testCase => {
22+
it('converts passed strings to regular expressions', () => {
23+
const actualResult = getConvertedExternals(testCase.input);
24+
expect(actualResult).toEqual(testCase.expectedOutput);
25+
});
26+
27+
it(`returns regular expressions which match expected modules and their subdirs, for input ${testCase.input}`, () => {
28+
[
29+
'nativescript-vue',
30+
'nativescript-vue/subdir',
31+
'nativescript-vue/subdir/subdir-2'
32+
].forEach(testString => {
33+
const regExpsExternals = getConvertedExternals(testCase.input);
34+
const result = regExpsExternals.some((regExp: RegExp) => !!regExp.exec(testString));
35+
expect(result).toBe(true, `String ${testString} does not match any of the regular expressions: ${regExpsExternals.join(', ')}`);
36+
});
37+
});
38+
39+
it(`returns regular expressions which does not match expected modules and their subdirs, for input ${testCase.input}`, () => {
40+
[
41+
'nativescript-facebook',
42+
'nativescript-facebook/nativescript-vue',
43+
'main-plugin/subdir/nativescript-vue',
44+
'nativescript-vue-template-compiler',
45+
'nativescript-vue-template-compiler/subdir'
46+
].forEach(testString => {
47+
const regExpsExternals = getConvertedExternals(testCase.input);
48+
const result = regExpsExternals.some((regExp: RegExp) => !!regExp.exec(testString));
49+
expect(result).toBe(false, `String ${testString} matches some of the regular expressions: ${regExpsExternals.join(', ')}`);
50+
});
51+
});
52+
});
53+
});
54+
});

‎package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,13 @@
100100
},
101101
"devDependencies": {
102102
"@ngtools/webpack": "~7.1.0",
103-
"@types/jasmine": "^3.3.1",
103+
"@types/jasmine": "^3.3.7",
104104
"@types/node": "^10.12.12",
105+
"@types/proxyquire": "1.3.28",
105106
"conventional-changelog-cli": "^1.3.22",
106107
"jasmine": "^3.2.0",
107108
"jasmine-spec-reporter": "^4.2.1",
109+
"proxyquire": "2.1.0",
108110
"typescript": "~3.1.1"
109111
}
110112
}

‎templates/webpack.angular.js‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,8 @@ module.exports = env => {
4646
sourceMap, // --env.sourceMap
4747
hmr, // --env.hmr,
4848
} = env;
49-
env.externals = env.externals || [];
50-
const externals = (env.externals).map((e) => { // --env.externals
51-
return new RegExp(e + ".*");
52-
});
5349

50+
const externals = nsWebpack.getConvertedExternals(env.externals);
5451
const appFullPath = resolve(projectRoot, appPath);
5552
const appResourcesFullPath = resolve(projectRoot, appResourcesPath);
5653

@@ -65,7 +62,7 @@ module.exports = env => {
6562
// when "@angular/core" is external, it's not included in the bundles. In this way, it will be used
6663
// directly from node_modules and the Angular modules loader won't be able to resolve the lazy routes
6764
// fixes https://github.com/NativeScript/nativescript-cli/issues/4024
68-
if (env.externals.indexOf("@angular/core") > -1) {
65+
if (externals.indexOf("@angular/core") > -1) {
6966
const appModuleRelativePath = getMainModulePath(resolve(appFullPath, entryModule));
7067
if (appModuleRelativePath) {
7168
const appModuleFolderPath = dirname(resolve(appFullPath, appModuleRelativePath));

‎templates/webpack.config.spec.ts‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import * as proxyquire from 'proxyquire';
2+
import * as nsWebpackIndex from '../index';
3+
// With noCallThru enabled, `proxyquire` will not fall back to requiring the real module to populate properties that are not mocked.
4+
// This allows us to mock packages that are not available in node_modules.
5+
// In case you want to enable fallback for a specific object, just add `'@noCallThru': false`.
6+
proxyquire.noCallThru();
7+
8+
class EmptyClass { };
9+
10+
const nativeScriptDevWebpack = {
11+
GenerateBundleStarterPlugin: EmptyClass,
12+
WatchStateLoggerPlugin: EmptyClass,
13+
PlatformFSPlugin: EmptyClass,
14+
getAppPath: () => 'app',
15+
getEntryModule: () => 'EntryModule',
16+
getResolver: () => null,
17+
getConvertedExternals: nsWebpackIndex.getConvertedExternals
18+
};
19+
20+
const emptyObject = {};
21+
22+
const webpackConfigAngular = proxyquire('./webpack.angular', {
23+
'nativescript-dev-webpack': nativeScriptDevWebpack,
24+
'nativescript-dev-webpack/nativescript-target': emptyObject,
25+
'nativescript-dev-webpack/transformers/ns-replace-bootstrap': emptyObject,
26+
'nativescript-dev-webpack/transformers/ns-replace-lazy-loader': emptyObject,
27+
'nativescript-dev-webpack/utils/ast-utils': emptyObject,
28+
'@ngtools/webpack': {
29+
AngularCompilerPlugin: EmptyClass
30+
}
31+
});
32+
33+
const webpackConfigTypeScript = proxyquire('./webpack.typescript', {
34+
'nativescript-dev-webpack': nativeScriptDevWebpack,
35+
'nativescript-dev-webpack/nativescript-target': emptyObject,
36+
});
37+
38+
const webpackConfigJavaScript = proxyquire('./webpack.javascript', {
39+
'nativescript-dev-webpack': nativeScriptDevWebpack,
40+
'nativescript-dev-webpack/nativescript-target': emptyObject,
41+
});
42+
43+
const webpackConfigVue = proxyquire('./webpack.vue', {
44+
'nativescript-dev-webpack': nativeScriptDevWebpack,
45+
'nativescript-dev-webpack/nativescript-target': emptyObject,
46+
'vue-loader/lib/plugin': EmptyClass,
47+
'nativescript-vue-template-compiler': emptyObject
48+
});
49+
50+
describe('webpack.config.js', () => {
51+
[
52+
{ type: 'javascript', webpackConfig: webpackConfigJavaScript },
53+
{ type: 'typescript', webpackConfig: webpackConfigTypeScript },
54+
{ type: 'angular', webpackConfig: webpackConfigAngular },
55+
{ type: 'vue', webpackConfig: webpackConfigVue }
56+
].forEach(element => {
57+
const { type, webpackConfig } = element;
58+
59+
describe(`verify externals for webpack.${type}.js`, () => {
60+
const getInput = (platform: string, externals: string[]) => {
61+
const input: any = { externals };
62+
input[platform] = true;
63+
return input;
64+
};
65+
66+
[
67+
'android',
68+
'ios'
69+
].forEach(platform => {
70+
describe(`for ${platform}`, () => {
71+
afterEach(() => {
72+
nativeScriptDevWebpack.getConvertedExternals = nsWebpackIndex.getConvertedExternals;
73+
});
74+
75+
it('returns empty array when externals are not passed', () => {
76+
const config = webpackConfig(getInput(platform, null));
77+
expect(config.externals).toEqual([]);
78+
});
79+
80+
it('calls getConvertedExternals to convert externals', () => {
81+
let isCalled = false;
82+
nativeScriptDevWebpack.getConvertedExternals = () => {
83+
isCalled = true;
84+
return [];
85+
};
86+
87+
const input = getInput(platform, ['nativescript-vue']);
88+
webpackConfig(input);
89+
expect(isCalled).toBe(true, 'Webpack.config.js must use the getConvertedExternals method');
90+
});
91+
92+
[
93+
{
94+
input: ['nativescript-vue'],
95+
expectedOutput: [/^nativescript-vue((\/.*)|$)/]
96+
},
97+
{
98+
input: ['nativescript-vue', 'nativescript-angular'],
99+
expectedOutput: [/^nativescript-vue((\/.*)|$)/, /^nativescript-angular((\/.*)|$)/]
100+
},
101+
].forEach(testCase => {
102+
const input = getInput(platform, testCase.input);
103+
104+
it(`are correct regular expressions, for input ${testCase.input}`, () => {
105+
const config = webpackConfig(input);
106+
expect(config.externals).toEqual(testCase.expectedOutput);
107+
});
108+
});
109+
});
110+
});
111+
});
112+
});
113+
});

‎templates/webpack.javascript.js‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ module.exports = env => {
4242
sourceMap, // --env.sourceMap
4343
hmr, // --env.hmr,
4444
} = env;
45-
const externals = (env.externals || []).map((e) => { // --env.externals
46-
return new RegExp(e + ".*");
47-
});
45+
const externals = nsWebpack.getConvertedExternals(env.externals);
4846

4947
const appFullPath = resolve(projectRoot, appPath);
5048
const appResourcesFullPath = resolve(projectRoot, appResourcesPath);

‎templates/webpack.typescript.js‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ module.exports = env => {
4242
sourceMap, // --env.sourceMap
4343
hmr, // --env.hmr,
4444
} = env;
45-
const externals = (env.externals || []).map((e) => { // --env.externals
46-
return new RegExp(e + ".*");
47-
});
45+
const externals = nsWebpack.getConvertedExternals(env.externals);
4846

4947
const appFullPath = resolve(projectRoot, appPath);
5048
const appResourcesFullPath = resolve(projectRoot, appResourcesPath);

‎templates/webpack.vue.js‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ module.exports = env => {
4646
hmr, // --env.hmr
4747
} = env;
4848

49-
const externals = (env.externals || []).map((e) => { // --env.externals
50-
return new RegExp(e + ".*");
51-
});
49+
const externals = nsWebpack.getConvertedExternals(env.externals);
5250

5351
const mode = production ? "production" : "development"
5452

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /