diff --git a/android-app-components-loader.js b/android-app-components-loader.js new file mode 100644 index 00000000..5e2ecfd3 --- /dev/null +++ b/android-app-components-loader.js @@ -0,0 +1,14 @@ +module.exports = function(source) { + this.cacheable(); + const { modules } = this.query; + const imports = modules.map(m => `require("${m}");`).join("\n"); + const augmentedSource = ` + if (!global["__snapshot"]) { + ${imports} + } + + ${source} + `; + + this.callback(null, augmentedSource); +}; diff --git a/demo/.gitignore b/demo/.gitignore index 90a487ea..829954a9 100644 --- a/demo/.gitignore +++ b/demo/.gitignore @@ -14,11 +14,6 @@ test-results.xml tsconfig.aot.json vendor.js -vendor-platform.android.js -vendor-platform.ios.js - vendor.ts -vendor-platform.android.ts -vendor-platform.ios.ts webpack.config.js diff --git a/demo/AngularApp/package.json b/demo/AngularApp/package.json index 6a565e5b..052fc6a0 100644 --- a/demo/AngularApp/package.json +++ b/demo/AngularApp/package.json @@ -13,24 +13,25 @@ } }, "dependencies": { - "@angular/common": "~5.2.0", - "@angular/compiler": "~5.2.0", - "@angular/core": "~5.2.0", - "@angular/forms": "~5.2.0", - "@angular/http": "~5.2.0", - "@angular/platform-browser": "~5.2.0", - "@angular/platform-browser-dynamic": "~5.2.0", - "@angular/router": "~5.2.0", - "nativescript-angular": "next", + "@angular/common": "~6.0.0-rc.0", + "@angular/compiler": "~6.0.0-rc.0", + "@angular/core": "~6.0.0-rc.0", + "@angular/forms": "~6.0.0-rc.0", + "@angular/http": "~6.0.0-rc.0", + "@angular/platform-browser": "~6.0.0-rc.0", + "@angular/platform-browser-dynamic": "~6.0.0-rc.0", + "@angular/router": "~6.0.0-rc.0", + "nativescript-angular": "rc", "nativescript-theme-core": "~1.0.2", "reflect-metadata": "~0.1.8", - "rxjs": "^5.5.0", + "rxjs": "~6.0.0-beta.1", "tns-core-modules": "next", "zone.js": "^0.8.4" }, "devDependencies": { - "@angular/compiler-cli": "~5.2.0", - "@ngtools/webpack": "~1.9.4", + "@angular-devkit/core": "~0.5.5", + "@angular/compiler-cli": "~6.0.0-rc.0", + "@ngtools/webpack": "~6.0.0-rc.3", "@types/chai": "^4.0.2", "@types/mocha": "^2.2.41", "@types/node": "^7.0.5", @@ -39,7 +40,8 @@ "babylon": "6.18.0", "chai": "~4.1.1", "chai-as-promised": "~7.1.1", - "copy-webpack-plugin": "~4.3.0", + "clean-webpack-plugin": "~0.1.19", + "copy-webpack-plugin": "~4.5.1", "css-loader": "~0.28.7", "extract-text-webpack-plugin": "~3.0.2", "lazy": "1.0.11", @@ -51,14 +53,15 @@ "nativescript-dev-typescript": "next", "nativescript-dev-webpack": "file:../..", "nativescript-worker-loader": "~0.8.1", - "node-sass": "^4.7.1", "raw-loader": "~0.5.1", - "resolve-url-loader": "~2.2.1", - "sass-loader": "^6.0.6", - "typescript": "~2.6.2", - "webpack": "~3.10.0", + "resolve-url-loader": "~2.3.0", + "sass-loader": "~6.0.6", + "typescript": "~2.7.2", + "uglifyjs-webpack-plugin": "~1.2.4", + "webpack": "~4.5.0", "webpack-bundle-analyzer": "^2.9.1", - "webpack-sources": "^1.1.0" + "webpack-cli": "~2.0.14", + "webpack-sources": "~1.1.0" }, "scripts": { "ns-bundle": "ns-bundle", diff --git a/demo/JavaScriptApp/package.json b/demo/JavaScriptApp/package.json index c60bed1d..c22d08a2 100644 --- a/demo/JavaScriptApp/package.json +++ b/demo/JavaScriptApp/package.json @@ -17,12 +17,16 @@ "tns-core-modules": "next" }, "devDependencies": { + "@types/chai": "^4.0.2", + "@types/mocha": "^2.2.41", + "@types/node": "^7.0.5", "babel-traverse": "6.26.0", "babel-types": "6.26.0", "babylon": "6.18.0", - "copy-webpack-plugin": "~4.0.1", + "clean-webpack-plugin": "~0.1.19", + "copy-webpack-plugin": "~4.5.1", "css-loader": "~0.28.7", - "extract-text-webpack-plugin": "~3.0.0", + "extract-text-webpack-plugin": "~3.0.2", "lazy": "1.0.11", "mocha": "~3.5.0", "mocha-junit-reporter": "^1.13.0", @@ -33,11 +37,13 @@ "nativescript-worker-loader": "~0.8.1", "node-sass": "^4.7.1", "raw-loader": "~0.5.1", - "resolve-url-loader": "~2.1.0", - "sass-loader": "^6.0.6", - "webpack": "~3.10.0", + "resolve-url-loader": "~2.3.0", + "sass-loader": "~6.0.6", + "uglifyjs-webpack-plugin": "~1.2.4", + "webpack": "~4.5.0", "webpack-bundle-analyzer": "^2.9.1", - "webpack-sources": "^1.1.0" + "webpack-cli": "~2.0.14", + "webpack-sources": "~1.1.0" }, "scripts": { "ns-bundle": "ns-bundle", diff --git a/demo/TypeScriptApp/package.json b/demo/TypeScriptApp/package.json index b21ac9c6..9521e498 100644 --- a/demo/TypeScriptApp/package.json +++ b/demo/TypeScriptApp/package.json @@ -17,16 +17,17 @@ "tns-core-modules": "next" }, "devDependencies": { - "awesome-typescript-loader": "~3.1.3", "@types/chai": "^4.0.2", "@types/mocha": "^2.2.41", "@types/node": "^7.0.5", + "awesome-typescript-loader": "~5.0.0-1", "babel-traverse": "6.26.0", "babel-types": "6.26.0", "babylon": "6.18.0", - "copy-webpack-plugin": "~4.0.1", + "clean-webpack-plugin": "~0.1.19", + "copy-webpack-plugin": "~4.5.1", "css-loader": "~0.28.7", - "extract-text-webpack-plugin": "~3.0.0", + "extract-text-webpack-plugin": "~3.0.2", "lazy": "1.0.11", "mocha": "~3.5.0", "mocha-junit-reporter": "^1.13.0", @@ -36,14 +37,15 @@ "nativescript-dev-typescript": "next", "nativescript-dev-webpack": "file:../..", "nativescript-worker-loader": "~0.8.1", - "node-sass": "^4.7.2", "raw-loader": "~0.5.1", - "resolve-url-loader": "~2.1.0", - "sass-loader": "^6.0.6", - "typescript": "~2.6.2", - "webpack": "~3.10.0", + "resolve-url-loader": "~2.3.0", + "sass-loader": "~6.0.6", + "typescript": "~2.7.2", + "uglifyjs-webpack-plugin": "~1.2.4", + "webpack": "~4.5.0", "webpack-bundle-analyzer": "^2.9.1", - "webpack-sources": "^1.1.0" + "webpack-cli": "~2.0.14", + "webpack-sources": "~1.1.0" }, "scripts": { "ns-bundle": "ns-bundle", diff --git a/dependencyManager.js b/dependencyManager.js index 6d292dcc..276ac23c 100644 --- a/dependencyManager.js +++ b/dependencyManager.js @@ -58,26 +58,28 @@ function addDependency(deps, name, version, force) { function getRequiredDeps(packageJson) { const deps = { - "webpack": "~3.10.0", + "webpack": "~4.5.0", + "webpack-cli": "~2.0.14", "webpack-bundle-analyzer": "^2.9.1", "webpack-sources": "~1.1.0", "clean-webpack-plugin": "~0.1.19", - "copy-webpack-plugin": "~4.3.0", + "copy-webpack-plugin": "~4.5.1", "raw-loader": "~0.5.1", "css-loader": "~0.28.7", "nativescript-worker-loader": "~0.8.1", - "resolve-url-loader": "~2.2.1", + "resolve-url-loader": "~2.3.0", "extract-text-webpack-plugin": "~3.0.2", - "uglifyjs-webpack-plugin": "~1.1.6", + "uglifyjs-webpack-plugin": "~1.2.4", }; if (isAngular({packageJson})) { Object.assign(deps, { "@angular/compiler-cli": packageJson.dependencies["@angular/core"], - "@ngtools/webpack": "~1.9.4", + "@ngtools/webpack": "~6.0.0-rc.3", + "@angular-devkit/core": "~0.5.5", }); } else if (isTypeScript({packageJson})) { - Object.assign(deps, { "awesome-typescript-loader": "~3.1.3" }); + Object.assign(deps, { "awesome-typescript-loader": "~5.0.0" }); } if (isSass({packageJson})) { diff --git a/lib/utils.js b/lib/utils.js index 1f524b19..f69a872d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -19,7 +19,7 @@ function buildEnvData($projectData, platform, env) { const appResourcesPath = getAppResourcesPathFromProjectData($projectData); Object.assign(envData, appPath && { appPath }, - appResourcesPath && { appResourcesPath } + appResourcesPath && { appResourcesPath }, ); return envData; diff --git a/nativescript-target/NsJsonpChunkTemplatePlugin.js b/nativescript-target/NsJsonpChunkTemplatePlugin.js index 2661dbb9..98c6a556 100644 --- a/nativescript-target/NsJsonpChunkTemplatePlugin.js +++ b/nativescript-target/NsJsonpChunkTemplatePlugin.js @@ -2,33 +2,47 @@ MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ -var ConcatSource = require("webpack-sources").ConcatSource; -var Template = require("webpack/lib/Template"); +"use strict"; -function JsonpChunkTemplatePlugin() { } -module.exports = JsonpChunkTemplatePlugin; - -JsonpChunkTemplatePlugin.prototype.apply = function (chunkTemplate) { +const ConcatSource = require("webpack-sources").ConcatSource; - //JSONP version - chunkTemplate.plugin("render", function (modules, chunk) { - var jsonpFunction = this.outputOptions.jsonpFunction; - var source = new ConcatSource(); - source.add(jsonpFunction + "(" + JSON.stringify(chunk.ids) + ","); - source.add(modules); - var entries = [chunk.entryModule].filter(Boolean).map(function (m) { - return m.id; +class JsonpChunkTemplatePlugin { + apply(chunkTemplate) { + chunkTemplate.hooks.render.tap( + "JsonpChunkTemplatePlugin", + (modules, chunk) => { + const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction; + const globalObject = chunkTemplate.outputOptions.globalObject; + const source = new ConcatSource(); + source.add( + `(${globalObject}[${JSON.stringify(jsonpFunction)}] = ${ + globalObject + }[${JSON.stringify(jsonpFunction)}] || []).push([${JSON.stringify( + chunk.ids + )},` + ); + source.add(modules); + const entries = [chunk.entryModule].filter(Boolean).map(m => + [m.id].concat( + Array.from(chunk.groupsIterable)[0] + .chunks.filter(c => c !== chunk) + .map(c => c.id) + ) + ); + if (entries.length > 0) { + source.add(`,${JSON.stringify(entries)}`); + } + source.add("])"); + return source; + } + ); + chunkTemplate.hooks.hash.tap("JsonpChunkTemplatePlugin", hash => { + hash.update("JsonpChunkTemplatePlugin"); + hash.update("4"); + hash.update(`${chunkTemplate.outputOptions.jsonpFunction}`); + hash.update(`${chunkTemplate.outputOptions.globalObject}`); }); - if (entries.length > 0) { - source.add("," + JSON.stringify(entries)); - } - source.add(")"); - return source; - }); - chunkTemplate.plugin("hash", function (hash) { - hash.update("JsonpChunkTemplatePlugin"); - hash.update("3"); - hash.update(this.outputOptions.jsonpFunction + ""); - hash.update(this.outputOptions.library + ""); - }); -}; + } +} +module.exports = JsonpChunkTemplatePlugin; + diff --git a/nativescript-target/NsJsonpHotUpdateChunkTemplatePlugin.js b/nativescript-target/NsJsonpHotUpdateChunkTemplatePlugin.js deleted file mode 100644 index 9fd49de5..00000000 --- a/nativescript-target/NsJsonpHotUpdateChunkTemplatePlugin.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var ConcatSource = require("webpack-sources").ConcatSource; -var Template = require("webpack/lib/Template"); - -function NsJsonpHotUpdateChunkTemplatePlugin() {} -module.exports = NsJsonpHotUpdateChunkTemplatePlugin; - -//JSONP version -NsJsonpHotUpdateChunkTemplatePlugin.prototype.apply = function(hotUpdateChunkTemplate) { - hotUpdateChunkTemplate.plugin("render", function(modulesSource, modules, removedModules, hash, id) { - var jsonpFunction = this.outputOptions.hotUpdateFunction; - var source = new ConcatSource(); - source.add(jsonpFunction + "(" + JSON.stringify(id) + ","); - source.add(modulesSource); - source.add(")"); - return source; - }); - hotUpdateChunkTemplate.plugin("hash", function(hash) { - hash.update("JsonpHotUpdateChunkTemplatePlugin"); - hash.update("3"); - hash.update(this.outputOptions.hotUpdateFunction + ""); - hash.update(this.outputOptions.library + ""); - }); -}; diff --git a/nativescript-target/NsJsonpMainTemplatePlugin.js b/nativescript-target/NsJsonpMainTemplatePlugin.js index 063ec2a9..3945dedf 100644 --- a/nativescript-target/NsJsonpMainTemplatePlugin.js +++ b/nativescript-target/NsJsonpMainTemplatePlugin.js @@ -1,165 +1,324 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var Template = require("webpack/lib/Template"); +const Template = require("webpack/lib/Template"); +const SyncWaterfallHook = require("tapable").SyncWaterfallHook; -function JsonpMainTemplatePlugin() { } -module.exports = JsonpMainTemplatePlugin; +class NsJsonpMainTemplatePlugin { + apply(mainTemplate) { + const needChunkOnDemandLoadingCode = chunk => { + for (const chunkGroup of chunk.groupsIterable) { + if (chunkGroup.getNumberOfChildren() > 0) return true; + } + return false; + }; + const needChunkLoadingCode = chunk => { + for (const chunkGroup of chunk.groupsIterable) { + if (chunkGroup.chunks.length > 1) return true; + if (chunkGroup.getNumberOfChildren() > 0) return true; + } + return false; + }; + const needEntryDeferringCode = chunk => { + for (const chunkGroup of chunk.groupsIterable) { + if (chunkGroup.chunks.length > 1) return true; + } + return false; + }; + if (!mainTemplate.hooks.jsonpScript) { + mainTemplate.hooks.jsonpScript = new SyncWaterfallHook([ + "source", + "chunk", + "hash" + ]); + } -JsonpMainTemplatePlugin.prototype.constructor = JsonpMainTemplatePlugin; -JsonpMainTemplatePlugin.prototype.apply = function (mainTemplate) { - mainTemplate.plugin("local-vars", function (source, chunk) { - if (chunk.chunks.length > 0) { - return this.asString([ - source, - "// objects to store loaded and loading chunks", - "var installedChunks = {", - this.indent( - chunk.ids.map(function (id) { - return id + ": 0"; - }).join(",\n") - ), - "};" - ]); - } - return source; - }); - - mainTemplate.plugin("require-ensure", function (_, chunk, hash) { - var chunkFilename = this.outputOptions.chunkFilename; - var chunkMaps = chunk.getChunkMaps(); - var insertMoreModules = [ - "var moreModules = chunk.modules, chunkIds = chunk.ids;", - "for(var moduleId in moreModules) {", - this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), - "}" - ]; + mainTemplate.hooks.localVars.tap( + "JsonpMainTemplatePlugin", + (source, chunk) => { + if (needChunkLoadingCode(chunk)) { + return Template.asString([ + source, + "", + "// object to store loaded and loading chunks", + "var installedChunks = {", + Template.indent( + chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n") + ), + "};", + "", + needEntryDeferringCode(chunk) ? "var deferredModules = [];" : "" + ]); + } + return source; + } + ); + mainTemplate.hooks.requireEnsure.tap( + "JsonpMainTemplatePlugin", + (source, chunk, hash) => { - var request = this.applyPluginsWaterfall("asset-path", JSON.stringify("./" + chunkFilename), { - hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", - hashWithLength: function (length) { - return "\" + " + this.renderCurrentHashCode(hash, length) + " + \""; - }.bind(this), - chunk: { - id: "\" + chunkId + \"", - hash: "\" + " + JSON.stringify(chunkMaps.hash) + "[chunkId] + \"", - hashWithLength: function (length) { - var shortChunkHashMap = {}; - Object.keys(chunkMaps.hash).forEach(function (chunkId) { - if (typeof chunkMaps.hash[chunkId] === "string") - shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length); - }); - return "\" + " + JSON.stringify(shortChunkHashMap) + "[chunkId] + \""; - }, - name: "\" + (" + JSON.stringify(chunkMaps.name) + "[chunkId]||chunkId) + \"" - } - }); + const chunkFilename = mainTemplate.outputOptions.chunkFilename; + const chunkMaps = chunk.getChunkMaps(); - return this.asString([ - "// \"0\" is the signal for \"already loaded\"", - "if(installedChunks[chunkId] !== 0) {", - this.indent([ - "var chunk = require(" + request + ");" - ]), - "}", - "return Promise.resolve();" - ]); - }); - mainTemplate.plugin("require-extensions", function (source, chunk) { - if (chunk.chunks.length === 0) return source; - return this.asString([ - source, - "", - "// on error function for async loading", - this.requireFn + ".oe = function(err) { console.error(err); throw err; };" - ]); - }); - mainTemplate.plugin("bootstrap", function (source, chunk, hash) { - if (chunk.chunks.length > 0) { - var jsonpFunction = this.outputOptions.jsonpFunction; - return this.asString([ - source, - "", - "// install a JSONP callback for chunk loading", - "var parentJsonpFunction = global[" + JSON.stringify(jsonpFunction) + "];", - "global[" + JSON.stringify(jsonpFunction) + "] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {", - this.indent([ - "// add \"moreModules\" to the modules object,", - "// then flag all \"chunkIds\" as loaded and fire callback", - "var moduleId, chunkId, i = 0, resolves = [], result;", - "for(;i < chunkIds.length; i++) {", - this.indent([ - "chunkId = chunkIds[i];", - "if(installedChunks[chunkId])", - this.indent("resolves.push(installedChunks[chunkId][0]);"), - "installedChunks[chunkId] = 0;" - ]), - "}", - "for(moduleId in moreModules) {", - this.indent([ - "if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {", - this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), - "}" - ]), - "}", - "if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);", - "while(resolves.length)", - this.indent("resolves.shift()();"), - this.entryPointInChildren(chunk) ? [ - "if(executeModules) {", - this.indent([ - "for(i=0; i < executeModules.length; i++) {", - this.indent("result = " + this.requireFn + "(" + this.requireFn + ".s = executeModules[i]);"), - "}" - ]), - "}", - "return result;", - ] : "" - ]), - "};" - ]); - } - return source; - }); - mainTemplate.plugin("hot-bootstrap", function (source, chunk, hash) { - var hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename; - var hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename; - var hotUpdateFunction = this.outputOptions.hotUpdateFunction; - var currentHotUpdateChunkFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateChunkFilename), { - hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", - hashWithLength: function (length) { - return "\" + " + this.renderCurrentHashCode(hash, length) + " + \""; - }.bind(this), - chunk: { - id: "\" + chunkId + \"" - } - }); - var currentHotUpdateMainFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateMainFilename), { - hash: "\" + " + this.renderCurrentHashCode(hash) + " + \"", - hashWithLength: function (length) { - return "\" + " + this.renderCurrentHashCode(hash, length) + " + \""; - }.bind(this) - }); + const request = mainTemplate.getAssetPath( + JSON.stringify(`./${chunkFilename}`), + { + hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, + hashWithLength: length => + `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`, + chunk: { + id: '" + chunkId + "', + hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, + hashWithLength(length) { + const shortChunkHashMap = Object.create(null); + for (const chunkId of Object.keys(chunkMaps.hash)) { + if (typeof chunkMaps.hash[chunkId] === "string") + shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr( + 0, + length + ); + } + return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`; + }, + name: `" + (${JSON.stringify( + chunkMaps.name + )}[chunkId]||chunkId) + "` + } + } + ); + + return Template.asString([ + source, + "", + "// JSONP chunk loading for javascript", + "", + "var installedChunkData = installedChunks[chunkId];", + 'if(installedChunkData !== 0) { // 0 means "already installed".', + Template.indent([`var chunk = require(${request})`]), + "}" + ]); + } + ); + mainTemplate.hooks.requireExtensions.tap( + "JsonpMainTemplatePlugin", + (source, chunk) => { + if (!needChunkOnDemandLoadingCode(chunk)) return source; + + return Template.asString([ + source, + "", + "// on error function for async loading", + `${ + mainTemplate.requireFn + }.oe = function(err) { console.error(err); throw err; };` + ]); + } + ); + mainTemplate.hooks.bootstrap.tap( + "JsonpMainTemplatePlugin", + (source, chunk, hash) => { + if (needChunkLoadingCode(chunk)) { + const withDefer = needEntryDeferringCode(chunk); + return Template.asString([ + source, + "", + "// install a JSONP callback for chunk loading", + "function webpackJsonpCallback(data) {", + Template.indent([ + "var chunkIds = data[0];", + "var moreModules = data[1];", + withDefer ? "var executeModules = data[2];" : "", + '// add "moreModules" to the modules object,', + '// then flag all "chunkIds" as loaded and fire callback', + "var moduleId, chunkId, i = 0, resolves = [];", + "for(;i < chunkIds.length; i++) {", + Template.indent([ + "chunkId = chunkIds[i];", + "if(installedChunks[chunkId]) {", + Template.indent("resolves.push(installedChunks[chunkId][0]);"), + "}", + "installedChunks[chunkId] = 0;" + ]), + "}", + "for(moduleId in moreModules) {", + Template.indent([ + "if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {", + Template.indent( + mainTemplate.renderAddModule( + hash, + chunk, + "moduleId", + "moreModules[moduleId]" + ) + ), + "}" + ]), + "}", + "if(parentJsonpFunction) parentJsonpFunction(data);", + "while(resolves.length) {", + Template.indent("resolves.shift()();"), + "}", + withDefer + ? Template.asString([ + "", + "// add entry modules from loaded chunk to deferred list", + "deferredModules.push.apply(deferredModules, executeModules || []);", + "", + "// run deferred modules when all chunks ready", + "return checkDeferredModules();" + ]) + : "" + ]), + "};", + withDefer + ? Template.asString([ + "function checkDeferredModules() {", + Template.indent([ + "var result;", + "for(var i = 0; i < deferredModules.length; i++) {", + Template.indent([ + "var deferredModule = deferredModules[i];", + "var fulfilled = true;", + "for(var j = 1; j < deferredModule.length; j++) {", + Template.indent([ + "var depId = deferredModule[j];", + "if(installedChunks[depId] !== 0) fulfilled = false;" + ]), + "}", + "if(fulfilled) {", + Template.indent([ + "deferredModules.splice(i--, 1);", + "result = " + + mainTemplate.requireFn + + "(" + + mainTemplate.requireFn + + ".s = deferredModule[0]);" + ]), + "}" + ]), + "}", + "return result;" + ]), + "}" + ]) + : "" + ]); + } + return source; + } + ); + mainTemplate.hooks.beforeStartup.tap( + "JsonpMainTemplatePlugin", + (source, chunk, hash) => { + if (needChunkLoadingCode(chunk)) { + var jsonpFunction = mainTemplate.outputOptions.jsonpFunction; + var globalObject = mainTemplate.outputOptions.globalObject; + return Template.asString([ + `var jsonpArray = ${globalObject}[${JSON.stringify( + jsonpFunction + )}] = ${globalObject}[${JSON.stringify(jsonpFunction)}] || [];`, + "var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);", + "jsonpArray.push = webpackJsonpCallback;", + "jsonpArray = jsonpArray.slice();", + "for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);", + "var parentJsonpFunction = oldJsonpFunction;", + "", + source + ]); + } + return source; + } + ); + mainTemplate.hooks.startup.tap( + "JsonpMainTemplatePlugin", + (source, chunk, hash) => { + if (needEntryDeferringCode(chunk)) { + if (chunk.hasEntryModule()) { + const entries = [chunk.entryModule].filter(Boolean).map(m => + [m.id].concat( + Array.from(chunk.groupsIterable)[0] + .chunks.filter(c => c !== chunk) + .map(c => c.id) + ) + ); + return Template.asString([ + "// add entry module to deferred list", + `deferredModules.push(${entries + .map(e => JSON.stringify(e)) + .join(", ")});`, + "// run deferred modules when ready", + "return checkDeferredModules();" + ]); + } else { + return Template.asString([ + "// run deferred modules from other chunks", + "checkDeferredModules();" + ]); + } + } + return source; + } + ); + mainTemplate.hooks.hotBootstrap.tap( + "JsonpMainTemplatePlugin", + (source, chunk, hash) => { + const globalObject = mainTemplate.outputOptions.globalObject; + const hotUpdateChunkFilename = + mainTemplate.outputOptions.hotUpdateChunkFilename; + const hotUpdateMainFilename = + mainTemplate.outputOptions.hotUpdateMainFilename; + const crossOriginLoading = + mainTemplate.outputOptions.crossOriginLoading; + const hotUpdateFunction = mainTemplate.outputOptions.hotUpdateFunction; + const currentHotUpdateChunkFilename = mainTemplate.getAssetPath( + JSON.stringify(hotUpdateChunkFilename), + { + hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, + hashWithLength: length => + `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`, + chunk: { + id: '" + chunkId + "' + } + } + ); + const currentHotUpdateMainFilename = mainTemplate.getAssetPath( + JSON.stringify(hotUpdateMainFilename), + { + hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, + hashWithLength: length => + `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "` + } + ); + const runtimeSource = Template.getFunctionContent( + require("./JsonpMainTemplate.runtime.js") + ) + .replace(/\/\/\$semicolon/g, ";") + .replace(/\$require\$/g, mainTemplate.requireFn) + .replace( + /\$crossOriginLoading\$/g, + crossOriginLoading + ? `script.crossOrigin = ${JSON.stringify(crossOriginLoading)}` + : "" + ) + .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename) + .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename) + .replace(/\$hash\$/g, JSON.stringify(hash)); + return `${source} +function hotDisposeChunk(chunkId) { + delete installedChunks[chunkId]; +} +var parentHotUpdateCallback = ${globalObject}[${JSON.stringify( + hotUpdateFunction + )}]; +${globalObject}[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`; + } + ); + mainTemplate.hooks.hash.tap("JsonpMainTemplatePlugin", hash => { + hash.update("jsonp"); + hash.update("5"); + hash.update(`${mainTemplate.outputOptions.globalObject}`); + hash.update(`${mainTemplate.outputOptions.chunkFilename}`); + hash.update(`${mainTemplate.outputOptions.jsonpFunction}`); + hash.update(`${mainTemplate.outputOptions.hotUpdateFunction}`); + }); + } +} +module.exports = NsJsonpMainTemplatePlugin; - return source + "\n" + - "function hotDisposeChunk(chunkId) {\n" + - "\tdelete installedChunks[chunkId];\n" + - "}\n" + - "var parentHotUpdateCallback = this[" + JSON.stringify(hotUpdateFunction) + "];\n" + - "this[" + JSON.stringify(hotUpdateFunction) + "] = " + Template.getFunctionContent(require("./JsonpMainTemplate.runtime.js")) - .replace(/\/\/\$semicolon/g, ";") - .replace(/\$require\$/g, this.requireFn) - .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename) - .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename) - .replace(/\$hash\$/g, JSON.stringify(hash)); - }); - mainTemplate.plugin("hash", function (hash) { - hash.update("jsonp"); - hash.update("4"); - hash.update(this.outputOptions.filename + ""); - hash.update(this.outputOptions.chunkFilename + ""); - hash.update(this.outputOptions.jsonpFunction + ""); - hash.update(this.outputOptions.hotUpdateFunction + ""); - }); -}; diff --git a/nativescript-target/NsJsonpTemplatePlugin.js b/nativescript-target/NsJsonpTemplatePlugin.js index 0841a3a8..18ee9cd7 100644 --- a/nativescript-target/NsJsonpTemplatePlugin.js +++ b/nativescript-target/NsJsonpTemplatePlugin.js @@ -1,17 +1,17 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var NsJsonpMainTemplatePlugin = require("./NsJsonpMainTemplatePlugin"); -var NsJsonpChunkTemplatePlugin = require("./NsJsonpChunkTemplatePlugin"); -var NsJsonpHotUpdateChunkTemplatePlugin = require("./NsJsonpHotUpdateChunkTemplatePlugin"); +const NsJsonpMainTemplatePlugin = require("./NsJsonpMainTemplatePlugin"); +const JsonpChunkTemplatePlugin = require("webpack/lib/web/JsonpChunkTemplatePlugin"); +const JsonpHotUpdateChunkTemplatePlugin = require("webpack/lib/web/JsonpHotUpdateChunkTemplatePlugin"); -function JsonpTemplatePlugin() {} -module.exports = JsonpTemplatePlugin; -JsonpTemplatePlugin.prototype.apply = function(compiler) { - compiler.plugin("this-compilation", function(compilation) { - compilation.mainTemplate.apply(new NsJsonpMainTemplatePlugin()); - compilation.chunkTemplate.apply(new NsJsonpChunkTemplatePlugin()); - compilation.hotUpdateChunkTemplate.apply(new NsJsonpHotUpdateChunkTemplatePlugin()); - }); -}; +class NsJsonpTemplatePlugin { + apply(compiler) { + compiler.hooks.thisCompilation.tap("NsJsonpTemplatePlugin", compilation => { + new NsJsonpMainTemplatePlugin().apply(compilation.mainTemplate); + new JsonpChunkTemplatePlugin().apply(compilation.chunkTemplate); + new JsonpHotUpdateChunkTemplatePlugin().apply( + compilation.hotUpdateChunkTemplate + ); + }) + } +} + +module.exports = NsJsonpTemplatePlugin; diff --git a/nativescript-target/NsNodeGlobalsPlugin.js b/nativescript-target/NsNodeGlobalsPlugin.js deleted file mode 100644 index 217c6ba0..00000000 --- a/nativescript-target/NsNodeGlobalsPlugin.js +++ /dev/null @@ -1,26 +0,0 @@ -// HACK: Prevent webpack from replacing "global" -// Fixes StackOverflow error caused by DefinePlugin with webpack 2.2+ - -var ConstDependency = require("webpack/lib/dependencies/ConstDependency"); - -function NsNodeGlobalsPlugin() {} -NsNodeGlobalsPlugin.prototype.apply = function(compiler) { - compiler.plugin("compilation", function(compilation, params) { - params.normalModuleFactory.plugin("parser", function(parser, parserOptions) { - parser.plugin("expression global", function(expr) { - var dep = new ConstDependency("global", expr.range); - dep.loc = expr.loc; - this.state.current.addDependency(dep); - return true; - }); - parser.plugin("expression __dirname", function(expr) { - var dep = new ConstDependency("__dirname", expr.range); - dep.loc = expr.loc; - this.state.current.addDependency(dep); - return true; - }); - }); - }); -}; - -module.exports = NsNodeGlobalsPlugin; diff --git a/nativescript-target/index.js b/nativescript-target/index.js index b426b223..3e483b33 100644 --- a/nativescript-target/index.js +++ b/nativescript-target/index.js @@ -1,20 +1,18 @@ module.exports = function nativescriptTarget(compiler) { - var options = this; - var webpackLib = "webpack/lib"; + const options = this; + const webpackLib = "webpack/lib"; - var NsNodeGlobalsPlugin = require("./NsNodeGlobalsPlugin"); // Custom template plugin - var NsJsonpTemplatePlugin = require("./NsJsonpTemplatePlugin"); + const NsJsonpTemplatePlugin = require("./NsJsonpTemplatePlugin"); - var FunctionModulePlugin = require(webpackLib + "/FunctionModulePlugin"); - var NodeSourcePlugin = require(webpackLib + "/node/NodeSourcePlugin"); - var LoaderTargetPlugin = require(webpackLib + "/LoaderTargetPlugin"); + const FunctionModulePlugin = require(webpackLib + "/FunctionModulePlugin"); + const NodeSourcePlugin = require(webpackLib + "/node/NodeSourcePlugin"); + const LoaderTargetPlugin = require(webpackLib + "/LoaderTargetPlugin"); compiler.apply( - new NsNodeGlobalsPlugin(), new NsJsonpTemplatePlugin(options.output), new FunctionModulePlugin(options.output), new NodeSourcePlugin(options.node), new LoaderTargetPlugin("web") ); -} \ No newline at end of file +} diff --git a/package.json b/package.json index 1acc420f..a32b8705 100644 --- a/package.json +++ b/package.json @@ -73,13 +73,13 @@ "request": "2.83.0", "schema-utils": "0.4.3", "semver": "5.4.1", - "shelljs": "0.6.0" + "shelljs": "0.6.0", + "tapable": "^1.0.0" }, "devDependencies": { - "@ngtools/webpack": "~1.9.0", + "@ngtools/webpack": "~6.0.0-rc.3", "@types/node": "^8.0.0", "source-map-support": "^0.5.0", - "typescript": "^2.6.1", - "conventional-changelog-cli": "1.3.21" + "typescript": "~2.7.2" } } diff --git a/platform-css-loader.js b/platform-css-loader.js deleted file mode 100644 index 0c58f1ca..00000000 --- a/platform-css-loader.js +++ /dev/null @@ -1,18 +0,0 @@ -var cssSuffixMatcher = /(@import.*)\.css/g; -var themeMatcher = /(@import[^'"]*)(['"]~?\/?)nativescript-theme-core(.*)/g; - -module.exports = function(source, sourcemap) { - var newSource = source.replace(cssSuffixMatcher, function (fullMatch, importPrefix) { - return importPrefix; - }).replace(themeMatcher, function(fullMatch, importPrefix, pathStart, rest) { - var quote = pathStart[0]; - return importPrefix + quote + "~nativescript-theme-core" + rest; - }); - - // Support for tests - if (this.callback) { - this.callback(null, newSource, sourcemap); - } else { - return newSource; - } -}; diff --git a/plugins/NativeScriptSnapshotPlugin/index.js b/plugins/NativeScriptSnapshotPlugin/index.js index 99e85baf..e0f7efd8 100644 --- a/plugins/NativeScriptSnapshotPlugin/index.js +++ b/plugins/NativeScriptSnapshotPlugin/index.js @@ -9,27 +9,23 @@ const schema = require("./options.json"); exports.NativeScriptSnapshotPlugin = (function() { function NativeScriptSnapshotPlugin(options) { NativeScriptSnapshotPlugin.validateSchema(options); + if (options.chunk) { + options.chunks = options.chunks || []; + options.chunks.push(options.chunk); + } - ProjectSnapshotGenerator.call(this, options); // Call the parent constructor + ProjectSnapshotGenerator.call(this, options); if (this.options.webpackConfig) { if (this.options.webpackConfig.output && this.options.webpackConfig.output.libraryTarget) { this.options.webpackConfig.output.libraryTarget = undefined; } - - if (this.options.webpackConfig.entry) { - if (typeof this.options.webpackConfig.entry === "string" || - this.options.webpackConfig.entry instanceof Array) - this.options.webpackConfig.entry = { bundle: this.options.webpackConfig.entry }; - } - - this.options.webpackConfig.entry["tns-java-classes"] = this.getTnsJavaClassesBuildPath(); } } NativeScriptSnapshotPlugin.validateSchema = function(options) { - if (!options.chunk) { - const error = NativeScriptSnapshotPlugin.extendError({ message: `No chunk specified!` }); + if (!options.chunk && !options.chunks) { + const error = NativeScriptSnapshotPlugin.extendError({ message: `No chunks specified!` }); throw error; } @@ -44,57 +40,45 @@ exports.NativeScriptSnapshotPlugin = (function() { NativeScriptSnapshotPlugin.prototype = Object.create(ProjectSnapshotGenerator.prototype); NativeScriptSnapshotPlugin.prototype.constructor = NativeScriptSnapshotPlugin; - NativeScriptSnapshotPlugin.prototype.getTnsJavaClassesBuildPath = function () { - return resolve(this.getBuildPath(), "../tns-java-classes.js"); - } - - NativeScriptSnapshotPlugin.prototype.generate = function (webpackChunk) { + NativeScriptSnapshotPlugin.prototype.generate = function (webpackChunks) { const options = this.options; - - const inputFile = join(options.webpackConfig.output.path, webpackChunk.files[0]); - - console.log(`\n Snapshotting bundle at ${inputFile}`); + const inputFiles = webpackChunks.map(chunk => join(options.webpackConfig.output.path, chunk.files[0])); + console.log(`\n Snapshotting bundle from ${inputFiles}`); const preparedAppRootPath = resolveAndroidAppPath(this.options.projectRoot); const preprocessedInputFile = join(preparedAppRootPath, "_embedded_script_.js"); return ProjectSnapshotGenerator.prototype.generate.call(this, { - inputFile, + inputFiles, preprocessedInputFile, targetArchs: options.targetArchs, useLibs: options.useLibs, androidNdkPath: options.androidNdkPath, v8Version: options.v8Version, - tnsJavaClassesPath: join(preparedAppRootPath, "tns-java-classes.js") }).then(() => { - // Make the original file empty - if (inputFile !== preprocessedInputFile) { - closeSync(openSync(inputFile, "w")); // truncates the input file content - } + // Make the original files empty + inputFiles.forEach(inputFile => + closeSync(openSync(inputFile, "w")) // truncates the input file content + ); }); } NativeScriptSnapshotPlugin.prototype.apply = function (compiler) { const options = this.options; - // Generate tns-java-classes.js file - debugger; - ProjectSnapshotGenerator.prototype.generateTnsJavaClassesFile.call(this, { - output: this.getTnsJavaClassesBuildPath(), - options: options.tnsJavaClassesOptions - }); - - // Generate snapshots compiler.plugin("after-emit", function (compilation, callback) { - debugger; - const chunkToSnapshot = compilation.chunks.find(chunk => chunk.name == options.chunk); - if (!chunkToSnapshot) { - const error = NativeScriptSnapshotPlugin.extendError({ message: `No chunk named '${options.chunk}' found.` }); + const chunksToSnapshot = options.chunks + .map(name => ({ name, chunk: compilation.chunks.find(chunk => chunk.name === name) })); + const unexistingChunks = chunksToSnapshot.filter(pair => !pair.chunk); + + if (unexistingChunks.length) { + const message = `The following chunks does not exist: ` + unexistingChunks.map(pair => pair.name).join(", "); + const error = NativeScriptSnapshotPlugin.extendError({ message }); compilation.errors.push(error); return callback(); } - this.generate(chunkToSnapshot) + this.generate(chunksToSnapshot.map(pair => pair.chunk)) .then(() => { console.log("Successfully generated snapshots!"); return callback(); diff --git a/plugins/NativeScriptSnapshotPlugin/options.json b/plugins/NativeScriptSnapshotPlugin/options.json index 8587fd49..a34fb015 100644 --- a/plugins/NativeScriptSnapshotPlugin/options.json +++ b/plugins/NativeScriptSnapshotPlugin/options.json @@ -4,6 +4,12 @@ "chunk": { "type": "string" }, + "chunks": { + "type": "array", + "items": { + "type": "string" + } + }, "projectRoot": { "type": "string" }, @@ -11,18 +17,6 @@ "targetArchs": { "type": "array" }, - "tnsJavaClassesOptions": { - "additionalProperties": false, - "type": "object", - "properties": { - "modules": { - "type": "array" - }, - "packages": { - "type": "array" - } - } - }, "useLibs": { "type": "boolean" }, diff --git a/plugins/PlatformFSPlugin.ts b/plugins/PlatformFSPlugin.ts index 1f11fd2a..d4ce3837 100644 --- a/plugins/PlatformFSPlugin.ts +++ b/plugins/PlatformFSPlugin.ts @@ -149,15 +149,15 @@ export function mapFileSystem(args: MapFileSystemArgs): any { const mappedFilesModified = filterIgnoredFilesAlienFilesAndMap(filesModified); - const mappedTimestamps = {}; + const mappedTimestamps = new Map(); for(const file in fileTimestamps) { const timestamp = fileTimestamps[file]; - mappedTimestamps[file] = timestamp; + mappedTimestamps.set(file, timestamp); const platformSuffixIndex = file.lastIndexOf(platformSuffix); if (platformSuffixIndex != -1) { const mappedFile = file.substr(0, platformSuffixIndex) + file.substr(platformSuffixIndex + platformSuffix.length - 1); if (!(mappedFile in fileTimestamps)) { - mappedTimestamps[mappedFile] = timestamp; + mappedTimestamps.set(mappedFile, timestamp); } } } diff --git a/projectFilesManager.js b/projectFilesManager.js index 0409b860..cb7d70a6 100644 --- a/projectFilesManager.js +++ b/projectFilesManager.js @@ -83,10 +83,7 @@ function getProjectTemplates(projectDir) { } function getAppTemplates(projectDir, appDir) { - const templates = { - "vendor-platform.android.ts": tsOrJs(projectDir, "vendor-platform.android"), - "vendor-platform.ios.ts": tsOrJs(projectDir, "vendor-platform.ios"), - }; + const templates = {}; if (isAngular({projectDir})) { templates["vendor.angular.ts"] = tsOrJs(projectDir, "vendor"); diff --git a/snapshot/android/project-snapshot-generator-cli-ags-parser.js b/snapshot/android/project-snapshot-generator-cli-ags-parser.js index 5207e075..76af75b1 100644 --- a/snapshot/android/project-snapshot-generator-cli-ags-parser.js +++ b/snapshot/android/project-snapshot-generator-cli-ags-parser.js @@ -4,12 +4,6 @@ module.exports = function parseProjectSnapshotGeneratorArgs() { if (result.targetArchs) { result.targetArchs = parseStringArray(result.targetArchs); } - if (result.tnsJavaClassesOptions && result.tnsJavaClassesOptions.packages !== undefined) { - result.tnsJavaClassesOptions.packages = parseStringArray(result.tnsJavaClassesOptions.packages); - } - if (result.tnsJavaClassesOptions && result.tnsJavaClassesOptions.modules !== undefined) { - result.tnsJavaClassesOptions.modules = parseStringArray(result.tnsJavaClassesOptions.modules); - } if (result.useLibs !== undefined) { result.useLibs = parseBool(result.useLibs); diff --git a/snapshot/android/project-snapshot-generator.js b/snapshot/android/project-snapshot-generator.js index a856e0fa..31813e87 100644 --- a/snapshot/android/project-snapshot-generator.js +++ b/snapshot/android/project-snapshot-generator.js @@ -5,7 +5,6 @@ const shelljs = require("shelljs"); const semver = require("semver"); const SnapshotGenerator = require("./snapshot-generator"); -const TnsJavaClassesGenerator = require("./tns-java-classes-generator"); const { CONSTANTS, createDirectory, @@ -89,11 +88,6 @@ ProjectSnapshotGenerator.installSnapshotArtefacts = function (projectRoot) { shelljs.mkdir("-p", configDestinationPath); shelljs.cp(join(buildPath, "include.gradle"), join(configDestinationPath, "include.gradle")); - // Copy tns-java-classes.js - if (shelljs.test("-e", join(buildPath, "tns-java-classes.js"))) { - shelljs.cp(join(buildPath, "tns-java-classes.js"), join(appPath, "tns-java-classes.js")); - } - if (shelljs.test("-e", join(buildPath, "ndk-build/libs"))) { // useLibs = true const libsDestinationPath = join(platformPath, "src", SnapshotGenerator.SNAPSHOT_PACKAGE_NANE, "jniLibs"); @@ -200,15 +194,6 @@ ProjectSnapshotGenerator.prototype.validateAndroidRuntimeVersion = function () { } } -ProjectSnapshotGenerator.prototype.generateTnsJavaClassesFile = function (generationOptions) { - const tnsJavaClassesGenerator = new TnsJavaClassesGenerator(); - return tnsJavaClassesGenerator.generate({ - projectRoot: this.options.projectRoot, - output: generationOptions.output, - options: generationOptions.options - }); -} - ProjectSnapshotGenerator.prototype.generate = function (generationOptions) { generationOptions = generationOptions || {}; @@ -219,17 +204,6 @@ ProjectSnapshotGenerator.prototype.generate = function (generationOptions) { shelljs.rm("-rf", this.getBuildPath()); shelljs.mkdir("-p", this.getBuildPath()); - // Generate tns-java-classes.js if needed - const tnsJavaClassesDestination = join(this.getBuildPath(), "tns-java-classes.js"); - if (generationOptions.tnsJavaClassesPath) { - if (generationOptions.tnsJavaClassesPath != tnsJavaClassesDestination) { - shelljs.cp(generationOptions.tnsJavaClassesPath, tnsJavaClassesDestination); - } - } - else { - this.generateTnsJavaClassesFile({ output: tnsJavaClassesDestination, options: generationOptions.tnsJavaClassesOptions }); - } - const snapshotToolsPath = resolveRelativePath(generationOptions.snapshotToolsPath) || CONSTANTS.SNAPSHOT_TMP_DIR; const androidNdkPath = generationOptions.androidNdkPath || process.env.ANDROID_NDK_HOME; @@ -246,15 +220,17 @@ ProjectSnapshotGenerator.prototype.generate = function (generationOptions) { throw new Error(noV8VersionFoundMessage); } - return generator.generate({ + const options = { snapshotToolsPath, - inputFile: generationOptions.inputFile || join(this.options.projectRoot, "__snapshot.js"), targetArchs: generationOptions.targetArchs || ["arm", "arm64", "ia32"], v8Version: generationOptions.v8Version || v8Version, preprocessedInputFile: generationOptions.preprocessedInputFile, useLibs: generationOptions.useLibs || false, - androidNdkPath - }).then(() => { + inputFiles: generationOptions.inputFiles || [join(this.options.projectRoot, "__snapshot.js")], + androidNdkPath, + }; + + return generator.generate(options).then(() => { console.log("Snapshots build finished succesfully!"); if (generationOptions.install) { diff --git a/snapshot/android/snapshot-generator-tools/bundle-preamble.js b/snapshot/android/snapshot-generator-tools/bundle-preamble.js index 910f5330..46e171f5 100644 --- a/snapshot/android/snapshot-generator-tools/bundle-preamble.js +++ b/snapshot/android/snapshot-generator-tools/bundle-preamble.js @@ -20,9 +20,9 @@ Object.defineProperty(global, "__snapshot", { global.__requireOverride = (function() { return function(moduleId, dirname) { /* - The android runtime laods in advance all JS modules that contain a native class successor generated statically at build time. - In case of snapshot this file always is the bundled one. Since it is snapshoted it is already laoded in the heap and is not meant - to be required. The tns-java-classes.js file is responsible for actually executing the modules containing native java classes. + The android runtime loads in advance all JS modules that contain a native class successor generated statically at build time. + In case of snapshot this file always is the bundled one. Since it is snapshoted it is already loaded in the heap and is not meant + to be required. The main entry file (bundle.js) is responsible for actually executing the modules containing native java classes. */ var resolvedModuleId = moduleId.replace(/^\.\/tns_modules\//, ""); if (resolvedModuleId === './_embedded_script_.js') { diff --git a/snapshot/android/snapshot-generator.js b/snapshot/android/snapshot-generator.js index a318c583..2f699a48 100644 --- a/snapshot/android/snapshot-generator.js +++ b/snapshot/android/snapshot-generator.js @@ -42,11 +42,13 @@ module.exports = SnapshotGenerator; SnapshotGenerator.SNAPSHOT_PACKAGE_NANE = "nativescript-android-snapshot"; -SnapshotGenerator.prototype.preprocessInputFile = function(inputFile, outputFile) { +SnapshotGenerator.prototype.preprocessInputFiles = function(inputFiles, outputFile) { // Make some modifcations on the original bundle and save it on the specified path const bundlePreambleContent = fs.readFileSync(BUNDLE_PREAMBLE_PATH, "utf8"); const bundleEndingContent = fs.readFileSync(BUNDLE_ENDING_PATH, "utf8"); - const snapshotFileContent = bundlePreambleContent + "\n" + fs.readFileSync(inputFile, "utf8") + "\n" + bundleEndingContent; + + const inputFilesContent = inputFiles.map(file => fs.readFileSync(file, "utf8")).join("\n"); + const snapshotFileContent = bundlePreambleContent + "\n" + inputFilesContent + "\n" + bundleEndingContent; fs.writeFileSync(outputFile, snapshotFileContent, { encoding: "utf8" }); } @@ -151,14 +153,11 @@ SnapshotGenerator.prototype.buildIncludeGradle = function() { SnapshotGenerator.prototype.generate = function(options) { // Arguments validation options = options || {}; - if (!options.inputFile) { throw new Error("inputFile option is not specified."); } - if (!shelljs.test("-e", options.inputFile)) { throw new Error("Can't find V8 snapshot input file: '" + options.inputFile + "'."); } - if (!options.targetArchs || options.targetArchs.length == 0) { throw new Error("No target archs specified."); } if (!options.v8Version) { throw new Error("No v8 version specified."); } if (!options.snapshotToolsPath) { throw new Error("snapshotToolsPath option is not specified."); } const preprocessedInputFile = options.preprocessedInputFile || join(this.buildPath, "inputFile.preprocessed"); - this.preprocessInputFile(options.inputFile, preprocessedInputFile); + this.preprocessInputFiles(options.inputFiles, preprocessedInputFile); // generates the actual .blob and .c files return this.runMksnapshotTool( diff --git a/snapshot/android/tns-java-classes-generator.js b/snapshot/android/tns-java-classes-generator.js deleted file mode 100644 index 296a806e..00000000 --- a/snapshot/android/tns-java-classes-generator.js +++ /dev/null @@ -1,66 +0,0 @@ -const fs = require("fs"); -const { join, dirname } = require("path"); -const shelljs = require("shelljs"); - -function TnsJavaClassesGenerator() {} -module.exports = TnsJavaClassesGenerator; - -TnsJavaClassesGenerator.prototype.generate = function(generationOptions) { - // Arguments validation - generationOptions = generationOptions || {}; - if (!generationOptions.projectRoot) { throw new Error("No projectRoot specified."); } - var initialSettings = generationOptions.options || { modules: [], packages: [] }; - initialSettings.modules = initialSettings.modules || []; - initialSettings.packages = initialSettings.packages || []; - - const packageJsonPath = join(generationOptions.projectRoot, "package.json"); - const nodeModulesPath = join(generationOptions.projectRoot, "node_modules"); - - /* - "tns-java-classes": { - "modules": ["packageX/moduleX", "./app/moduleY"], - "packages": ["package1", "package2"] - } - */ - var tnsJavaClassesSettings = this.getTnsJavaClassesSettings(packageJsonPath); - Array.prototype.push.apply(initialSettings.modules, tnsJavaClassesSettings.modules); - Array.prototype.push.apply(initialSettings.packages, tnsJavaClassesSettings.packages); - - var nodeModules = fs.readdirSync(nodeModulesPath).filter((moduleName) => initialSettings.packages.indexOf(moduleName) >= 0); - for(var i = 0; i < nodeModules.length; i++) { - var moduleName = nodeModules[i]; - var modulePackageJsonPath = join(nodeModulesPath, moduleName, "package.json"); - var moduleTnsJavaClassesSettings = this.getTnsJavaClassesSettings(modulePackageJsonPath); - // Backward compatibilty with modules 3.0.1 and below - if (moduleName == "tns-core-modules" && moduleTnsJavaClassesSettings.modules.length == 0) { - moduleTnsJavaClassesSettings = { modules: ["ui/frame/activity", "ui/frame/fragment"] }; - } - Array.prototype.push.apply(initialSettings.modules, moduleTnsJavaClassesSettings.modules); - } - - // Generate the file - var tnsJavaClassesFileContent = initialSettings.modules.map(moduleName => "require(\"" + moduleName + "\");").join("\n"); - if (generationOptions.output) { - shelljs.mkdir("-p", dirname(generationOptions.output)); - if (generationOptions.outputAppend) { - var currentFileContent = shelljs.test("-e", generationOptions.output) ? fs.readFileSync(generationOptions.output, "utf8") : ""; - tnsJavaClassesFileContent = currentFileContent + tnsJavaClassesFileContent; - } - fs.writeFileSync(generationOptions.output, tnsJavaClassesFileContent, { encoding: "utf8" }); - } - return tnsJavaClassesFileContent; -} - -TnsJavaClassesGenerator.prototype.getTnsJavaClassesSettings = function(packageJsonPath) { - var packageJson = shelljs.test("-e", packageJsonPath) ? JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) : {}; - if (packageJson.snapshot && - packageJson.snapshot.android && - packageJson.snapshot.android["tns-java-classes"]) { - var extendedJavaClasses = packageJson.snapshot.android["tns-java-classes"]; - extendedJavaClasses.modules = extendedJavaClasses.modules || []; - extendedJavaClasses.packages = extendedJavaClasses.packages || []; - return extendedJavaClasses; - } - - return { modules: [], packages: [] }; -} \ No newline at end of file diff --git a/templates/vendor-platform.android.ts b/templates/vendor-platform.android.ts deleted file mode 100644 index 719f2649..00000000 --- a/templates/vendor-platform.android.ts +++ /dev/null @@ -1,9 +0,0 @@ -require("application"); -if (!global["__snapshot"]) { - // In case snapshot generation is enabled these modules will get into the bundle - // but will not be required/evaluated. - // The snapshot webpack plugin will add them to the tns-java-classes.js bundle file. - // This way, they will be evaluated on app start as early as possible. - require("ui/frame"); - require("ui/frame/activity"); -} diff --git a/templates/vendor-platform.ios.ts b/templates/vendor-platform.ios.ts deleted file mode 100644 index 5ce49d81..00000000 --- a/templates/vendor-platform.ios.ts +++ /dev/null @@ -1,3 +0,0 @@ -// There is a bug in angular: https://github.com/angular/angular-cli/pull/8589/files -// Legendary stuff, its webpack plugin pretty much doesn't work with empty TypeScript files in v1.8.3 -void 0; diff --git a/templates/vendor.angular.ts b/templates/vendor.angular.ts index 4526eca9..fd29170f 100644 --- a/templates/vendor.angular.ts +++ b/templates/vendor.angular.ts @@ -5,8 +5,6 @@ const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sas global.registerWebpackModules(appCssContext); application.loadAppCss(); -require("./vendor-platform"); - require("reflect-metadata"); require("@angular/platform-browser"); require("@angular/core"); diff --git a/templates/vendor.nativescript.ts b/templates/vendor.nativescript.ts index 8a381374..b35ccd2a 100644 --- a/templates/vendor.nativescript.ts +++ b/templates/vendor.nativescript.ts @@ -5,6 +5,4 @@ const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sas global.registerWebpackModules(appCssContext); application.loadAppCss(); -require("./vendor-platform"); - require("bundle-entry-points"); diff --git a/templates/webpack.angular.js b/templates/webpack.angular.js index e82a3b85..0e2a1b87 100644 --- a/templates/webpack.angular.js +++ b/templates/webpack.angular.js @@ -1,4 +1,4 @@ -const { relative, resolve, join } = require("path"); +const { join, relative, resolve, sep } = require("path"); const webpack = require("webpack"); const nsWebpack = require("nativescript-dev-webpack"); @@ -43,7 +43,14 @@ module.exports = env => { const appFullPath = resolve(projectRoot, appPath); const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + const entryModule = aot ? + nsWebpack.getAotEntryModule(appFullPath) : + `${nsWebpack.getEntryModule(appFullPath)}.ts`; + const entryPath = `.${sep}${entryModule}`; + const vendorPath = `.${sep}vendor.ts`; + const config = { + mode: "development", context: appFullPath, watchOptions: { ignored: [ @@ -54,16 +61,15 @@ module.exports = env => { }, target: nativescriptTarget, entry: { - bundle: aot ? - `./${nsWebpack.getAotEntryModule(appFullPath)}` : - `./${nsWebpack.getEntryModule(appFullPath)}`, - vendor: "./vendor", + bundle: entryPath, + vendor: vendorPath, }, output: { - pathinfo: true, + pathinfo: false, path: dist, libraryTarget: "commonjs2", filename: "[name].js", + globalObject: "global", }, resolve: { extensions: [".ts", ".js", ".scss", ".css"], @@ -88,6 +94,30 @@ module.exports = env => { "timers": false, "setImmediate": false, "fs": "empty", + "__dirname": false, + }, + devtool: "none", + optimization: { + runtimeChunk: { name: "vendor" }, + splitChunks: { + cacheGroups: { + common: { + name: "common", + chunks: "all", + test: /vendor/, + enforce: true, + }, + } + }, + minimize: !!uglify, + minimizer: [ + // Override default minimizer to work around an Android issue by setting compress = false + new UglifyJsPlugin({ + uglifyOptions: { + compress: platform !== "android" + } + }) + ], }, module: { rules: [ @@ -114,17 +144,15 @@ module.exports = env => { { test: /\.scss$/, exclude: /[\/|\\]app\.scss$/, use: ["raw-loader", "resolve-url-loader", "sass-loader"] }, // Compile TypeScript files with ahead-of-time compiler. - { test: /.ts$/, use: [ - "nativescript-dev-webpack/moduleid-compat-loader", - { loader: "@ngtools/webpack", options: ngToolsWebpackOptions }, - ]}, + { + test: /.ts$/, use: [ + "nativescript-dev-webpack/moduleid-compat-loader", + { loader: "@ngtools/webpack", options: ngToolsWebpackOptions }, + ] + }, ], }, plugins: [ - // Vendor libs go to the vendor.js chunk - new webpack.optimize.CommonsChunkPlugin({ - name: ["vendor"], - }), // Define useful constants like TNS_WEBPACK new webpack.DefinePlugin({ "global.TNS_WEBPACK": "true", @@ -133,11 +161,11 @@ module.exports = env => { new CleanWebpackPlugin([ `${dist}/**/*` ]), // Copy native app resources to out dir. new CopyWebpackPlugin([ - { - from: `${appResourcesFullPath}/${appResourcesPlatformDir}`, - to: `${dist}/App_Resources/${appResourcesPlatformDir}`, - context: projectRoot - }, + { + from: `${appResourcesFullPath}/${appResourcesPlatformDir}`, + to: `${dist}/App_Resources/${appResourcesPlatformDir}`, + context: projectRoot + }, ]), // Copy assets to out dir. Add your own globs as needed. new CopyWebpackPlugin([ @@ -149,6 +177,7 @@ module.exports = env => { // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ "./vendor", + "./common", "./bundle", ]), // Support for web workers since v3.2 @@ -161,7 +190,6 @@ module.exports = env => { platformOptions: { platform, platforms, - // ignore: ["App_Resources"] }, }, ngToolsWebpackOptions) ), @@ -169,6 +197,25 @@ module.exports = env => { new nsWebpack.WatchStateLoggerPlugin(), ], }; + + if (platform === "android") { + // Add your custom Activities, Services and other android app components here. + const appComponents = [ + "tns-core-modules/ui/frame", + "tns-core-modules/ui/frame/activity", + ]; + + // Require all Android app components + // in the entry module (bundle.ts) and the vendor module (vendor.ts). + config.module.rules.unshift({ + test: new RegExp(`${entryPath}|${vendorPath}`), + use: { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + } + }); + } + if (report) { // Generate report files for bundles content config.plugins.push(new BundleAnalyzerPlugin({ @@ -179,27 +226,16 @@ module.exports = env => { statsFilename: resolve(projectRoot, "report", `stats.json`), })); } + if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ - chunk: "vendor", + chunks: [ "vendor", "common" ], projectRoot, webpackConfig: config, targetArchs: ["arm", "arm64", "ia32"], - tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, useLibs: false })); } - if (uglify) { - config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true })); - - // Work around an Android issue by setting compress = false - const compress = platform !== "android"; - config.plugins.push(new UglifyJsPlugin({ - uglifyOptions: { - mangle: { reserved: nsWebpack.uglifyMangleExcludes }, // Deprecated. Remove if using {N} 4+. - compress, - } - })); - } + return config; }; diff --git a/templates/webpack.javascript.js b/templates/webpack.javascript.js index aa51a4fe..75ee94f3 100644 --- a/templates/webpack.javascript.js +++ b/templates/webpack.javascript.js @@ -1,4 +1,4 @@ -const { relative, resolve, join } = require("path"); +const { join, relative, resolve, sep } = require("path"); const webpack = require("webpack"); const nsWebpack = require("nativescript-dev-webpack"); @@ -41,7 +41,12 @@ module.exports = env => { const appFullPath = resolve(projectRoot, appPath); const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + const entryModule = nsWebpack.getEntryModule(appFullPath); + const entryPath = `.${sep}${entryModule}.js`; + const vendorPath = `.${sep}vendor.js`; + const config = { + mode: "development", context: appFullPath, watchOptions: { ignored: [ @@ -52,14 +57,15 @@ module.exports = env => { }, target: nativescriptTarget, entry: { - bundle: `./${nsWebpack.getEntryModule(appFullPath)}`, - vendor: "./vendor" + bundle: entryPath, + vendor: vendorPath, }, output: { - pathinfo: true, + pathinfo: false, path: dist, libraryTarget: "commonjs2", filename: "[name].js", + globalObject: "global", }, resolve: { extensions: [".js", ".scss", ".css"], @@ -84,6 +90,30 @@ module.exports = env => { "timers": false, "setImmediate": false, "fs": "empty", + "__dirname": false, + }, + devtool: "none", + optimization: { + runtimeChunk: { name: "vendor" }, + splitChunks: { + cacheGroups: { + common: { + name: "common", + chunks: "all", + test: /vendor/, + enforce: true, + }, + } + }, + minimize: !!uglify, + minimizer: [ + // Override default minimizer to work around an Android issue by setting compress = false + new UglifyJsPlugin({ + uglifyOptions: { + compress: platform !== "android" + } + }) + ], }, module: { rules: [ @@ -104,10 +134,6 @@ module.exports = env => { ] }, plugins: [ - // Vendor libs go to the vendor.js chunk - new webpack.optimize.CommonsChunkPlugin({ - name: ["vendor"], - }), // Define useful constants like TNS_WEBPACK new webpack.DefinePlugin({ "global.TNS_WEBPACK": "true", @@ -131,8 +157,9 @@ module.exports = env => { ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ - "./vendor", - "./bundle", + "./vendor", // install webpackJsonpCallback + "./common", // require app/vendor.js + "./bundle", // require the entry module (app/app.js) ]), // Support for web workers since v3.2 new NativeScriptWorkerPlugin(), @@ -144,6 +171,25 @@ module.exports = env => { new nsWebpack.WatchStateLoggerPlugin(), ], }; + + if (platform === "android") { + // Add your custom Activities, Services and other android app components here. + const appComponents = [ + "tns-core-modules/ui/frame", + "tns-core-modules/ui/frame/activity", + ]; + + // Require all Android app components + // in the entry module (bundle.js) and the vendor module (vendor.js). + config.module.rules.unshift({ + test: new RegExp(`${entryPath}|${vendorPath}`), + use: { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + } + }); + } + if (report) { // Generate report files for bundles content config.plugins.push(new BundleAnalyzerPlugin({ @@ -154,27 +200,19 @@ module.exports = env => { statsFilename: resolve(projectRoot, "report", `stats.json`), })); } + if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ - chunk: "vendor", + chunks: [ + "common", + "vendor", + ], projectRoot, webpackConfig: config, targetArchs: ["arm", "arm64", "ia32"], - tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, useLibs: false })); } - if (uglify) { - config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true })); - - // Work around an Android issue by setting compress = false - const compress = platform !== "android"; - config.plugins.push(new UglifyJsPlugin({ - uglifyOptions: { - mangle: { reserved: nsWebpack.uglifyMangleExcludes }, // Deprecated. Remove if using {N} 4+. - compress, - } - })); - } + return config; }; diff --git a/templates/webpack.typescript.js b/templates/webpack.typescript.js index cfc2be63..dfedb7de 100644 --- a/templates/webpack.typescript.js +++ b/templates/webpack.typescript.js @@ -1,4 +1,4 @@ -const { relative, resolve, join } = require("path"); +const { join, relative, resolve, sep } = require("path"); const webpack = require("webpack"); const nsWebpack = require("nativescript-dev-webpack"); @@ -41,7 +41,12 @@ module.exports = env => { const appFullPath = resolve(projectRoot, appPath); const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + const entryModule = nsWebpack.getEntryModule(appFullPath); + const entryPath = `.${sep}${entryModule}.ts`; + const vendorPath = `.${sep}vendor.ts`; + const config = { + mode: "development", context: appFullPath, watchOptions: { ignored: [ @@ -52,14 +57,15 @@ module.exports = env => { }, target: nativescriptTarget, entry: { - bundle: `./${nsWebpack.getEntryModule(appFullPath)}`, - vendor: "./vendor" + bundle: entryPath, + vendor: vendorPath, }, output: { - pathinfo: true, + pathinfo: false, path: dist, libraryTarget: "commonjs2", filename: "[name].js", + globalObject: "global", }, resolve: { extensions: [".ts", ".js", ".scss", ".css"], @@ -69,7 +75,7 @@ module.exports = env => { "node_modules", ], alias: { - '~': resolve("./app") + '~': appFullPath }, // don't resolve symlinks to symlinked modules symlinks: false @@ -84,6 +90,30 @@ module.exports = env => { "timers": false, "setImmediate": false, "fs": "empty", + "__dirname": false, + }, + devtool: "none", + optimization: { + runtimeChunk: { name: "vendor" }, + splitChunks: { + cacheGroups: { + common: { + name: "common", + chunks: "all", + test: /vendor/, + enforce: true, + }, + } + }, + minimize: !!uglify, + minimizer: [ + // Override default minimizer to work around an Android issue by setting compress = false + new UglifyJsPlugin({ + uglifyOptions: { + compress: platform !== "android" + } + }) + ], }, module: { rules: [ @@ -106,10 +136,6 @@ module.exports = env => { ] }, plugins: [ - // Vendor libs go to the vendor.js chunk - new webpack.optimize.CommonsChunkPlugin({ - name: ["vendor"], - }), // Define useful constants like TNS_WEBPACK new webpack.DefinePlugin({ "global.TNS_WEBPACK": "true", @@ -133,8 +159,9 @@ module.exports = env => { ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ - "./vendor", - "./bundle", + "./vendor", // install webpackJsonpCallback + "./common", // require app/vendor.js + "./bundle", // require the entry module (app/app.js) ]), // Support for web workers since v3.2 new NativeScriptWorkerPlugin(), @@ -146,6 +173,25 @@ module.exports = env => { new nsWebpack.WatchStateLoggerPlugin(), ], }; + + if (platform === "android") { + // Add your custom Activities, Services and other android app components here. + const appComponents = [ + "tns-core-modules/ui/frame", + "tns-core-modules/ui/frame/activity", + ]; + + // Require all Android app components + // in the entry module (bundle.ts) and the vendor module (vendor.ts). + config.module.rules.unshift({ + test: new RegExp(`${entryPath}|${vendorPath}`), + use: { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + } + }); + } + if (report) { // Generate report files for bundles content config.plugins.push(new BundleAnalyzerPlugin({ @@ -156,27 +202,19 @@ module.exports = env => { statsFilename: resolve(projectRoot, "report", `stats.json`), })); } + if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ - chunk: "vendor", + chunks: [ + "common", + "vendor", + ], projectRoot, webpackConfig: config, targetArchs: ["arm", "arm64", "ia32"], - tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, useLibs: false })); } - if (uglify) { - config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true })); - - // Work around an Android issue by setting compress = false - const compress = platform !== "android"; - config.plugins.push(new UglifyJsPlugin({ - uglifyOptions: { - mangle: { reserved: nsWebpack.uglifyMangleExcludes }, // Deprecated. Remove if using {N} 4+. - compress, - } - })); - } + return config; }; diff --git a/tns-aot-loader.js b/tns-aot-loader.js deleted file mode 100644 index 3d9d116b..00000000 --- a/tns-aot-loader.js +++ /dev/null @@ -1,16 +0,0 @@ -function fixRelativeImports(fileName, source) { - let result = source; - - result = result.replace(/(\.\.\/)+platform\'/g, 'platform\''); - result = result.replace(/(\.\.\/)+ui\/frame\'/g, 'ui/frame\''); - result = result.replace(/(\.\.\/)+ui\/page\'/g, 'ui/page\''); - - return result; -} - - -module.exports = function (source, map) { - this.cacheable(); - var resultSource = fixRelativeImports(this.resource, source); - this.callback(null, resultSource, map); -}; \ No newline at end of file