Skip to content

Commit cc8d44c

Browse files
authored
feat: webpack5 (#45)
1 parent 18dd1bc commit cc8d44c

17 files changed

+7972
-182
lines changed

.gitignore

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
.DS_Store
22

3+
test-app/
4+
35
*.js
46
*.js.map
7+
!nativescript.webpack.js
8+
!loaders/unit-test-loader.js
59

610
coverage
711
lib-cov
@@ -32,4 +36,5 @@ npm-debug.log
3236
node_modules
3337
.d.ts
3438

35-
platforms/android/nativescript_unit_test_runner.aar
39+
platforms/android/nativescript_unit_test_runner.aar
40+
platforms/android/unit_test_runner.aar

app/app.css

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.landingContainer {
2-
background-color: #3C5AFD;
2+
background-color: #65ADF1;
33
}
44

55
.landingBackground {
@@ -19,13 +19,8 @@
1919
color: white;
2020
vertical-align: bottom;
2121
}
22-
23-
.title {
24-
background-color: #3C5AFD;
25-
}
26-
2722
.statusBar {
28-
background-color: #3C5AFD;
23+
background-color: #65ADF1;
2924
color: white;
3025
}
3126

app/bundle-app.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
1+
declare let require: any;
2+
13
import { Application } from "@nativescript/core";
4+
import "./app.css"
5+
6+
const context = require.context('./', true, /.*\.(js|css|xml)/)
7+
global.registerWebpackModules(context);
8+
29
Application.run({ moduleName: "bundle-app-root" });

app/main-page.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded">
22
<GridLayout cssClass="landingContainer">
3-
<Image src="{{ imageSrc }}" stretch="none" cssClass="landingBackground"/>
3+
<Image src="{{ imageSrc }}" stretch="none" class="landingBackground"/>
44

5-
<Label text="{{ serverInfo }}" cssClass="landingText"/>
5+
<Label text="{{ serverInfo }}" class="landingText"/>
66
</GridLayout>
77
</Page>

app/main-view-model.ts

+1-1
Large diffs are not rendered by default.

app/services/karma-files-service.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
export class KarmaFilesService {
2-
private extensionRegex = /\.([^.\/]+)$/;
3-
private appPrefix = null;
4-
private testsPrefix = null;
5-
private nodeModulesPrefix = null;
6-
private bundle = false;
2+
private readonly extensionRegex = /\.([^.\/]+)$/;
3+
private readonly appPrefix = null;
4+
private readonly testsPrefix = null;
5+
private readonly absoluteTestsPrefix = null;
6+
private readonly nodeModulesPrefix = null;
7+
private readonly bundle: boolean = false;
78

89
constructor(private http, config: IHostConfiguration) {
910
this.appPrefix = `/base/${config.options.appDirectoryRelativePath}/`;
1011
this.testsPrefix = `/base/${config.options.appDirectoryRelativePath}/tests`;
12+
this.absoluteTestsPrefix = `/absolute`;
1113
this.nodeModulesPrefix = `/base/node_modules/`;
1214
this.bundle = config.options.bundle;
1315
}
1416

1517
public getServedFilesData(baseUrl: string): Promise<IScriptInfo[]> {
1618
const contextUrl = `${baseUrl}/context.json`;
1719
console.log("NSUTR: downloading " + contextUrl);
18-
19-
const result = this.http.getString(contextUrl)
20+
21+
return this.http.getString(contextUrl)
2022
.then(content => {
21-
var parsedContent: IKarmaContext = JSON.parse(content);
23+
const parsedContent: IKarmaContext = JSON.parse(content);
2224
return parsedContent.files;
2325
})
2426
.then(scriptUrls => {
@@ -37,14 +39,12 @@ export class KarmaFilesService {
3739
url,
3840
type,
3941
contents,
40-
shouldEval: !extension || extension.toLowerCase() === "js"
42+
shouldEval: !extension || extension.toLowerCase() === "js" || extension.toLowerCase() === "ts"
4143
};
4244
});
4345
}
4446
}));
4547
});
46-
47-
return result;
4848
}
4949

5050
private getScriptData(url: string): { extension: string, localPath: string, type: ScriptTypes } {
@@ -65,7 +65,7 @@ export class KarmaFilesService {
6565
private getScriptType(url: string): ScriptTypes {
6666
let type = ScriptTypes.CodeUnderTestType;
6767

68-
if (url.startsWith(this.testsPrefix)) {
68+
if (url.startsWith(this.testsPrefix) || url.startsWith(this.absoluteTestsPrefix)) {
6969
type = ScriptTypes.TestType;
7070
} else if (url.startsWith(this.nodeModulesPrefix)) {
7171
type = ScriptTypes.FrameworkAdapterType;
@@ -86,4 +86,4 @@ export class KarmaFilesService {
8686

8787
return localPath;
8888
}
89-
}
89+
}

app/services/karma-host-resolver.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ export class KarmaHostResolver implements IKarmaHostResolver {
22
constructor(private http) { }
33

44
public resolveKarmaHost(ips: string[], port: number): Promise<string> {
5-
const result = new Promise<string>(resolve => {
6-
var foundKarma = false;
7-
var resolvers = ips.map(ip => {
8-
var karmaClientUrl = `http://${ip}:${port}/context.json`;
5+
return new Promise<string>(resolve => {
6+
let foundKarma = false;
7+
8+
const resolvers = ips.map(ip => {
9+
const karmaClientUrl = `http://${ip}:${port}/context.json`;
910
console.log('NSUTR: fetching ' + karmaClientUrl);
11+
1012
return this.http.getString({
1113
url: karmaClientUrl,
1214
method: 'GET',
@@ -19,14 +21,13 @@ export class KarmaHostResolver implements IKarmaHostResolver {
1921
}
2022
}, () => undefined)
2123
});
24+
2225
Promise.all(resolvers)
2326
.then(() => {
2427
if (!foundKarma) {
2528
resolve(null);
2629
}
2730
})
2831
});
29-
30-
return result;
3132
}
32-
}
33+
}

app/services/test-execution-service.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export class TestExecutionService implements ITestExecutionService {
2727
// WARNING in ../node_modules/@nativescript/unit-test-runner/main-view-model.js 204:28-53
2828
// Critical dependency: the request of a dependency is an expression
2929
if (!global.TNS_WEBPACK) {
30-
require(script.localPath);
30+
// @ts-ignore
31+
require(script.localPath);
3132
}
3233
} else {
3334
if (script.shouldEval) {
@@ -78,4 +79,4 @@ export class TestExecutionService implements ITestExecutionService {
7879
delete global.define;
7980
}
8081
}
81-
}
82+
}

app/test-run-page.xml

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<Page xmlns="http://www.nativescript.org/tns.xsd" navigatedTo="pageNavigatedTo">
22
<GridLayout rows="*,auto,8*,auto,auto">
3-
<GridLayout row="0" columns="*,auto,*" cssClass="title">
4-
<Image src="{{ imageSrc }}" stretch="aspectFill" col="1"/>
3+
<GridLayout row="0" columns="*,auto,*" class="title">
4+
<Image src="{{ imageSrc }}" stretch="aspectFit" col="1" width="150"/>
55
</GridLayout>
66

7-
<Progress row="1" value="{{ testsRan }}" maxValue="{{ testsTotal }}" cssClass="testProgress"/>
7+
<Progress row="1" value="{{ testsRan }}" maxValue="{{ testsTotal }}" class="testProgress"/>
88

99
<StackLayout row="2" style="margin: 20px;">
10-
<Label text="{{ testsTotal, 'Total: ' + testsTotal}} " cssClass="testsTotal"/>
11-
<Label text="{{ testsPassed, 'Passed: ' + testsPassed }}" cssClass="testsPassed"/>
12-
<Label text="{{ testsFailed, 'Failed: ' + testsFailed }}" cssClass="testsFailed"/>
10+
<Label text="{{ testsTotal, 'Total: ' + testsTotal}} " class="testsTotal"/>
11+
<Label text="{{ testsPassed, 'Passed: ' + testsPassed }}" class="testsPassed"/>
12+
<Label text="{{ testsFailed, 'Failed: ' + testsFailed }}" class="testsFailed"/>
1313
<StackLayout orientation="horizontal" visibility="{{ testsRunning, testsRunning ? 'visible' : 'collapsed' }}" horizontalAlignment="left">
1414
<ActivityIndicator busy="true"/>
1515
<Label text="Testing in progress..." verticalAlignment="center"/>
@@ -18,8 +18,8 @@
1818

1919
<Button text="View Test Run Details" tap="{{ viewTestRunDetails }}" row="3"/>
2020

21-
<GridLayout row="4" cssClass="statusBar">
22-
<Label text="{{ serverInfo }}" cssClass="statusText"/>
21+
<GridLayout row="4" class="statusBar">
22+
<Label text="{{ serverInfo }}" class="statusText"/>
2323
</GridLayout>
2424
</GridLayout>
2525
</Page>

loaders/unit-test-loader.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const {relative, extname, sep} = require('path');
2+
3+
/**
4+
* Unit Test Loader
5+
*
6+
* Replaces test source with a loadModule call
7+
* This allows us to use eval to run the tests
8+
* and pre-bundle them with the app.
9+
*/
10+
module.exports = function unitTestLoader(source, map) {
11+
const opts = this.getOptions();
12+
const testPathRelativeToAppPath = relative(opts.appPath, this.resourcePath);
13+
const ext = extname(testPathRelativeToAppPath);
14+
const loadPath = testPathRelativeToAppPath.replace(ext, "").replace(sep, '/'); // use forward slash always
15+
16+
source = `
17+
// UNIT-TEST-LOADER START
18+
try {
19+
console.log('Loading test: ${loadPath}');
20+
global.loadModule('${loadPath}');
21+
} catch(err) {
22+
console.log('Failed to load test ${loadPath}.', err)
23+
throw new Error('Failed to load test ${loadPath}.');
24+
}
25+
// UNIT-TEST-LOADER END
26+
`;
27+
28+
this.callback(null, source, map);
29+
};

makeTestApp.sh

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/sh
2+
3+
npm pack
4+
5+
rm -rf test-app
6+
ns create test-app --tsc
7+
cd test-app
8+
9+
ns test init --framework jasmine
10+
11+
npm i ../nativescript-unit-test-runner-*.tgz --legacy-peer-deps
12+
# todo: change...
13+
npm i ~/Code/NativeScript/packages/webpack5/nativescript-webpack-5.0.0-dev.tgz --legacy-peer-deps
14+
15+
rm webpack.config.js
16+
./node_modules/.bin/nativescript-webpack init
17+

nativescript.webpack.js

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
const { join, dirname } = require('path');
2+
const { merge } = require('webpack-merge');
3+
const globRegex = require('glob-regex');
4+
5+
function getKarmaTestsRegex(webpack) {
6+
const karmaConfig = require(webpack.Utils.project.getProjectFilePath('karma.conf.js'));
7+
let filesRegex = karmaConfig.filesRegex ||
8+
new RegExp((karmaConfig.filePatterns || []).map((glob) =>
9+
globRegex(`./${glob}`).source // all webpack require.context start with `./` and glob-regex adds ^
10+
).join('|'));
11+
12+
if (!filesRegex || !filesRegex.source) {
13+
webpack.Utils.log.warn("Karma files regex not found, falling back to tests/**/*.ts");
14+
filesRegex = /tests\/.*\.ts/;
15+
}
16+
return filesRegex;
17+
}
18+
19+
/**
20+
* @param {typeof import("@nativescript/webpack")} webpack
21+
*/
22+
module.exports = webpack => {
23+
webpack.chainWebpack((config, env) => {
24+
if (env.karmaWebpack) {
25+
return setupKarmaBuild(config, env, webpack);
26+
}
27+
28+
if (env.unitTesting) {
29+
return setupUnitTestBuild(config, env, webpack);
30+
}
31+
});
32+
};
33+
34+
/**
35+
* @param {import("webpack-chain")} config
36+
* @param {typeof import("@nativescript/webpack")} webpack
37+
*/
38+
function setupKarmaBuild(config, env, webpack) {
39+
const karmaWebpack = require('karma-webpack/lib/webpack/defaults');
40+
const defaults = karmaWebpack.create();
41+
delete defaults.optimization;
42+
43+
karmaWebpack.create = () => {
44+
return defaults;
45+
};
46+
47+
config.entryPoints.clear();
48+
config.optimization.clear();
49+
50+
config.plugins.delete('WatchStatePlugin');
51+
if (config.plugins.has('AngularCompilerPlugin')) {
52+
config.plugins.delete('AngularCompilerPlugin');
53+
config.module.rules.delete('angular');
54+
}
55+
// config.plugins.delete('CleanWebpackPlugin')
56+
57+
58+
config.output.delete('path'); // use temp path
59+
config.output.set('iife', true);
60+
config.output.set('libraryTarget', 'global');
61+
config.output.set('clean', true);
62+
63+
config.module
64+
.rule('unit-test')
65+
.include.add(webpack.Utils.platform.getEntryDirPath()).end()
66+
.test(/\.(ts|js)/)
67+
.use('unit-test-loader')
68+
.loader(join(__dirname, 'loaders', 'unit-test-loader'))
69+
.options({
70+
appPath: webpack.Utils.platform.getEntryDirPath(),
71+
});
72+
}
73+
74+
/**
75+
* @param {import("webpack-chain")} config
76+
* @param {typeof import("@nativescript/webpack")} webpack
77+
*/
78+
function setupUnitTestBuild(config, env, webpack) {
79+
// config.plugins.delete('CleanWebpackPlugin');
80+
// config.output.set('clean', false);
81+
82+
// harmless warnings
83+
config.set(
84+
'ignoreWarnings',
85+
(config.get('ignoreWarnings') || []).concat([
86+
/Can't resolve '@nativescript\/unit-test-runner\/app\/stop-process.js'/
87+
])
88+
);
89+
90+
const runnerPath = dirname(
91+
require.resolve('@nativescript/unit-test-runner/package.json')
92+
);
93+
config.module.rule('css').include.add(runnerPath);
94+
config.module.rule('xml').include.add(runnerPath);
95+
config.module.rule('js').include.add(runnerPath);
96+
const filesRegex = getKarmaTestsRegex(webpack);
97+
98+
config.plugin('DefinePlugin').tap((args) => {
99+
args[0] = merge(args[0], {
100+
'global.TNS_WEBPACK': true,
101+
});
102+
103+
return args;
104+
});
105+
106+
const entryPath = webpack.Utils.virtualModules.addVirtualEntry(config, 'unit-test-runner', `
107+
// VIRTUAL ENTRY START
108+
const context = require.context(
109+
"~/",
110+
/* deep: */ true,
111+
/* filter: */ ${filesRegex}
112+
);
113+
global.registerWebpackModules(context);
114+
// VIRTUAL ENTRY END
115+
`);
116+
117+
// config.entryPoints.clear()
118+
config.entry('bundle')
119+
.clear()
120+
.add('@nativescript/core/globals/index.js')
121+
.add('@nativescript/core/bundle-entry-points')
122+
.add('@nativescript/unit-test-runner/app/bundle-app')
123+
// .add('@nativescript/unit-test-runner/app/entry')
124+
.add(entryPath);
125+
if (webpack.Utils.platform.getPlatformName() === 'android') {
126+
config.entry('bundle')
127+
.add('@nativescript/core/ui/frame')
128+
.add('@nativescript/core/ui/frame/activity');
129+
}
130+
}

0 commit comments

Comments
 (0)