diff --git a/bundle-config-loader.js b/bundle-config-loader.js new file mode 100644 index 00000000..bbec9d1b --- /dev/null +++ b/bundle-config-loader.js @@ -0,0 +1,21 @@ +module.exports = function(source) { + this.cacheable(); + const { registerPages, loadCss } = this.query; + + if (registerPages) { + source = ` + require("nativescript-dev-webpack/register-modules"); + ${source} + `; + } + + if (loadCss) { + source = ` + require("nativescript-dev-webpack/load-application-css"); + ${source} + `; + } + + this.callback(null, source); +}; + diff --git a/demo/JavaScriptApp/app/app.js b/demo/JavaScriptApp/app/app.js index 0f9c0327..3294b628 100644 --- a/demo/JavaScriptApp/app/app.js +++ b/demo/JavaScriptApp/app/app.js @@ -4,7 +4,6 @@ You can use this file to perform app-level initialization, but the primary purpose of the file is to pass control to the app’s first module. */ -require("./bundle-config"); var application = require("application"); application.start({ moduleName: "main-page" }); diff --git a/demo/JavaScriptApp/app/bundle-config.js b/demo/JavaScriptApp/app/bundle-config.js deleted file mode 100644 index 18b6c2eb..00000000 --- a/demo/JavaScriptApp/app/bundle-config.js +++ /dev/null @@ -1,9 +0,0 @@ -if (global.TNS_WEBPACK) { - // registers tns-core-modules UI framework modules - require("bundle-entry-points"); - - // register application modules - // This will register each xml, css, js, ts, scss etc. in the app/ folder - const context = require.context("~/", true, /page\.(xml|css|js|ts|scss)$/); - global.registerWebpackModules(context); -} diff --git a/demo/TypeScriptApp/app/app.ts b/demo/TypeScriptApp/app/app.ts index e445c167..25278f50 100644 --- a/demo/TypeScriptApp/app/app.ts +++ b/demo/TypeScriptApp/app/app.ts @@ -4,7 +4,6 @@ You can use this file to perform app-level initialization, but the primary purpose of the file is to pass control to the app’s first module. */ -import "./bundle-config"; import * as app from 'application'; app.start({ moduleName: 'main-page' }); diff --git a/demo/TypeScriptApp/app/bundle-config.ts b/demo/TypeScriptApp/app/bundle-config.ts deleted file mode 100644 index 589d3ca8..00000000 --- a/demo/TypeScriptApp/app/bundle-config.ts +++ /dev/null @@ -1,9 +0,0 @@ -if ((global).TNS_WEBPACK) { - //registers tns-core-modules UI framework modules - require("bundle-entry-points"); - - // register application modules - // This will register each xml, css, js, ts, scss etc. in the app/ folder - const context = (require).context("~/", true, /page\.(xml|css|js|ts|scss)$/); - global.registerWebpackModules(context); -} diff --git a/demo/TypeScriptApp/tsconfig.json b/demo/TypeScriptApp/tsconfig.json index 1e991d80..4e5e97a3 100644 --- a/demo/TypeScriptApp/tsconfig.json +++ b/demo/TypeScriptApp/tsconfig.json @@ -15,6 +15,9 @@ "*": [ "./node_modules/tns-core-modules/*", "./node_modules/*" + ], + "~/*": [ + "app/*" ] } }, diff --git a/index.js b/index.js index faeb625e..7c5aa25f 100644 --- a/index.js +++ b/index.js @@ -32,9 +32,6 @@ exports.getAotEntryModule = function (appDirectory) { return aotEntry; } -// Exported for backwards compatibility with {N} 3 -exports.uglifyMangleExcludes = require("./mangle-excludes"); - exports.getEntryModule = function (appDirectory) { verifyEntryModuleDirectory(appDirectory); diff --git a/installer.js b/installer.js index 2ce9d7de..b068fca5 100644 --- a/installer.js +++ b/installer.js @@ -4,10 +4,9 @@ const dependencyManager = require("./dependencyManager"); function install() { const projectDir = helpers.getProjectDir(); - const appPath = helpers.getAppPath(); const packageJson = helpers.getPackageJson(projectDir); - projectFilesManager.addProjectFiles(projectDir, appPath); + projectFilesManager.addProjectFiles(projectDir); const postinstallOptions = dependencyManager.addProjectDeps(packageJson); packageJson.devDependencies = postinstallOptions.deps; @@ -19,8 +18,7 @@ function install() { function uninstall() { const projectDir = helpers.getProjectDir(); - const appPath = helpers.getAppPath(); - projectFilesManager.removeProjectFiles(projectDir, appPath); + projectFilesManager.removeProjectFiles(projectDir); console.log("NativeScript Webpack removed!"); } diff --git a/templates/vendor.nativescript.ts b/load-application-css.js similarity index 76% rename from templates/vendor.nativescript.ts rename to load-application-css.js index b35ccd2a..bc8b8fb9 100644 --- a/templates/vendor.nativescript.ts +++ b/load-application-css.js @@ -1,8 +1,6 @@ -// Snapshot the ~/app.css and the theme const application = require("application"); require("ui/styling/style-scope"); const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/); global.registerWebpackModules(appCssContext); application.loadAppCss(); -require("bundle-entry-points"); diff --git a/mangle-excludes.js b/mangle-excludes.js deleted file mode 100644 index f48e8eab..00000000 --- a/mangle-excludes.js +++ /dev/null @@ -1,140 +0,0 @@ -module.exports = [ - // Control names - "AbsoluteLayout", - "ActionBar", - "ActivityIndicator", - "Button", - "DatePicker", - "DockLayout", - "EditableTextBase", - "FlexboxLayout", - "GridLayout", - "Image", - "Label", - "Layout", - "LayoutBase", - "ListPicker", - "ListView", - "Page", - "Progress", - "SearchBar", - "SegmentedBar", - "Slider", - "StackLayout", - "Switch", - "TabView", - "TextBase", - "TextField", - "TextView", - "TimePicker", - "View", - "WrapLayout", - - // Android native class extenders - "BroadcastReceiver", - "CustomTypefaceSpan", - "DialogFragmentClassInner", - "FragmentClass", - "ListViewAdapter", - "LruBitmapCache", - "NativeScriptActivity", - "OurTabHost", - "PageChangedListener", - "PagerAdapterClassInner", - "PinchGestureListener", - "SegmentedBarColorDrawable", - "SwipeGestureListener", - "TapAndDoubleTapGestureListener", - "WebViewClientClassInner", - - // tns 3.0 - "DisableUserInteractionListener", - "EditorActionListener", - "FocusChangeListener", - "TextWatcher", - "ValueChangeListenerImpl", - - "AnimationListnerImpl", - "AnimationListenerImpl", - "TransitionListenerImpl", - "CheckedChangeListenerImpl", - "ClickListenerImpl", - "CompatCloseListenerImpl", - "CloseListenerImpl", - "DateChangedListenerImpl", - "DialogFragmentImpl", - "EditTextListenersImpl", - "FormatterImpl", - "ImageLoadedListenerImpl", - "ItemClickListenerImpl", - "MenuItemClickListenerImpl", - "NSCacheDelegateImpl", - "NativeViewGroupImpl", - "PageChangedListenerImpl", - "PagerAdapterImpl", - "PinchGestureListenerImpl", - "CompatQueryTextListenerImpl", - "QueryTextListenerImpl", - "SeekBarChangeListenerImpl", - "SwipeGestureListenerImpl", - "TabChangeListenerImpl", - "TabContentFactoryImpl", - "TabHostImpl", - "TapAndDoubleTapGestureListenerImpl", - "TextTransformationImpl", - "TimeChangedListenerImpl", - "TouchListenerImpl", - "WebViewClientImpl", - - // iOS native class extenders - "AnimatedTransitioning", - "AnimationDelegateImpl", - "DataSource", - "FrameHandlerImpl", - "ListPickerDataSource", - "ListPickerDelegateImpl", - "ListViewCell", - "LocationListenerImpl", - "NSURLSessionTaskDelegateImpl", - "NotificationObserver", - "ObserverClass", - "Responder", - "SelectionHandlerImpl", - "SliderChangeHandlerImpl", - "SwitchChangeHandlerImpl", - "TapBarItemHandlerImpl", - "TapHandlerImpl", - "TimerTargetImpl", - "TouchGestureRecognizer", - "TransitionDelegate", - "UIActionSheetDelegateImpl", - "UIAlertViewDelegateImpl", - "UIDatePickerChangeHandlerImpl", - "UIDocumentInteractionControllerDelegateImpl", - "UIGestureRecognizerDelegateImpl", - "UIGestureRecognizerImpl", - "UIImagePickerControllerDelegateImpl", - "UINavigationControllerAnimatedDelegate", - "UINavigationControllerDelegateImpl", - "UINavigationControllerImpl", - "UIScrollViewDelegateImpl", - "UISearchBarDelegateImpl", - "UITabBarControllerDelegateImpl", - "UITabBarControllerImpl", - "UITableViewDelegateImpl", - "UITableViewRowHeightDelegateImpl", - "UITextFieldDelegateImpl", - "UITextFieldImpl", - "UITextViewDelegateImpl", - "UITimePickerChangeHandlerImpl", - "UIViewControllerImpl", - "UIWebViewDelegateImpl", - "Window", - - // Sidedrawer transitions - // Should be removed after {N} 4.0 is released - // See: https://github.com/telerik/nativescript-ui-feedback/issues/477#issuecomment-360772046 - "PushTransition", - "FadeTransition", - "SlideInOnTopTransition", -]; diff --git a/nsCliHelpers.js b/nsCliHelpers.js deleted file mode 100644 index 13633fd1..00000000 --- a/nsCliHelpers.js +++ /dev/null @@ -1,50 +0,0 @@ -const { getPath } = require("global-modules-path"); - -const PROJECT_DATA_GETTERS = { - appPath: "getAppDirectoryRelativePath", - appResourcesPath: "getAppResourcesRelativeDirectoryPath", -}; - -function getProjectData(projectDir) { - const cli = getNsCli(); - if (!cli) { - return {}; - } - - const projectDataService = cli.projectDataService; - const projectData = safeGet(projectDataService, "getProjectData", projectDir); - - return projectData; -} - -function getNsCli() { - const cliPath = getPath("nativescript", "tns"); - if (!cliPath) { - return; - } - - const cli = require(cliPath); - - return cli; -} - -function safeGet(object, property, ...args) { - if (!object) { - return; - } - - const value = object[property]; - if (!value) { - return; - } - - return typeof value === "function" ? - value.bind(object)(...args) : - value; -} - -module.exports = { - PROJECT_DATA_GETTERS, - getProjectData, - safeGet, -}; diff --git a/plugins/NativeScriptSnapshotPlugin/index.js b/plugins/NativeScriptSnapshotPlugin/index.js index e0f7efd8..f489aa14 100644 --- a/plugins/NativeScriptSnapshotPlugin/index.js +++ b/plugins/NativeScriptSnapshotPlugin/index.js @@ -1,28 +1,62 @@ -const { resolve, join } = require("path"); -const { closeSync, openSync } = require("fs"); +const { relative, resolve, join } = require("path"); +const { closeSync, openSync, writeFileSync } = require("fs"); const validateOptions = require("schema-utils"); const ProjectSnapshotGenerator = require("../../snapshot/android/project-snapshot-generator"); -const { resolveAndroidAppPath } = require("../../projectHelpers"); +const { resolveAndroidAppPath, getAndroidProjectPath } = require("../../projectHelpers"); const schema = require("./options.json"); +const SNAPSHOT_ENTRY_NAME = "snapshot-entry"; +const SNAPSHOT_ENTRY_MODULE = `${SNAPSHOT_ENTRY_NAME}.js`; + 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); - if (this.options.webpackConfig) { - if (this.options.webpackConfig.output && this.options.webpackConfig.output.libraryTarget) { - this.options.webpackConfig.output.libraryTarget = undefined; - } + const { webpackConfig } = this.options; + NativeScriptSnapshotPlugin.removeLibraryTarget(webpackConfig); + + const { entry } = webpackConfig; + if (typeof entry === "string" || Array.isArray(entry)) { + webpackConfig.entry = { bundle: entry }; + } + + NativeScriptSnapshotPlugin.ensureSnapshotModuleEntry(this.options); + } + + NativeScriptSnapshotPlugin.removeLibraryTarget = function(webpackConfig) { + const { output } = webpackConfig; + if (output) { + output.libraryTarget = undefined; } } + NativeScriptSnapshotPlugin.ensureSnapshotModuleEntry = function(options) { + const { webpackConfig, requireModules, chunks, projectRoot, includeApplicationCss } = options; + + const androidProjectPath = getAndroidProjectPath({ projectRoot: projectRoot }); + const snapshotEntryPath = join(androidProjectPath, SNAPSHOT_ENTRY_MODULE); + + let snapshotEntryContent = ""; + if (includeApplicationCss) { + snapshotEntryContent += `require("nativescript-dev-webpack/load-application-css");`; + } + snapshotEntryContent += requireModules.map(mod => `require('${mod}')`).join(";"); + + writeFileSync(snapshotEntryPath, snapshotEntryContent, { encoding: "utf8" }); + + // add the module to the entry points to make sure it's content is evaluated + webpackConfig.entry[SNAPSHOT_ENTRY_NAME] = relative(webpackConfig.context, snapshotEntryPath); + + // prepend the module to the script that will be snapshotted + chunks.unshift(SNAPSHOT_ENTRY_NAME); + + // ensure that the runtime is installed only in the snapshotted chunk + webpackConfig.optimization.runtimeChunk = { name: SNAPSHOT_ENTRY_NAME }; + } + NativeScriptSnapshotPlugin.validateSchema = function(options) { if (!options.chunk && !options.chunks) { const error = NativeScriptSnapshotPlugin.extendError({ message: `No chunks specified!` }); @@ -31,12 +65,16 @@ exports.NativeScriptSnapshotPlugin = (function() { try { validateOptions(schema, options, "NativeScriptSnapshotPlugin"); + + if (options.chunk) { + options.chunks = options.chunks || []; + options.chunks.push(options.chunk); + } } catch (error) { throw new Error(error.message); } } - // inherit ProjectSnapshotGenerator NativeScriptSnapshotPlugin.prototype = Object.create(ProjectSnapshotGenerator.prototype); NativeScriptSnapshotPlugin.prototype.constructor = NativeScriptSnapshotPlugin; diff --git a/plugins/NativeScriptSnapshotPlugin/options.json b/plugins/NativeScriptSnapshotPlugin/options.json index a34fb015..8432973e 100644 --- a/plugins/NativeScriptSnapshotPlugin/options.json +++ b/plugins/NativeScriptSnapshotPlugin/options.json @@ -13,16 +13,44 @@ "projectRoot": { "type": "string" }, - "webpackConfig": {}, + "webpackConfig": { + "type": "object" + }, "targetArchs": { - "type": "array" + "type": "array", + "default": [ + "arm", + "arm64", + "ia32" + ], + "items": { + "type": "string", + "enum": [ + "arm", + "arm64", + "ia32" + ] + } }, "useLibs": { - "type": "boolean" + "type": "boolean", + "default": false }, "v8Version": { - "type": "string" + "type": "string" + }, + "requireModules": { + "type": "array", + "default": [] + }, + "includeApplicationCss": { + "type": "boolean", + "default": true } }, + "required": [ + "projectRoot", + "webpackConfig" + ], "additionalProperties": false } diff --git a/projectFilesManager.js b/projectFilesManager.js index cb7d70a6..4fe8750e 100644 --- a/projectFilesManager.js +++ b/projectFilesManager.js @@ -3,38 +3,26 @@ const fs = require("fs"); const { isTypeScript, isAngular } = require("./projectHelpers"); -function addProjectFiles(projectDir, appDir) { +function addProjectFiles(projectDir) { const projectTemplates = getProjectTemplates(projectDir); Object.keys(projectTemplates).forEach(function(templateName) { const templateDestination = projectTemplates[templateName]; templateName = path.resolve(templateName); copyTemplate(templateName, templateDestination); }); - - const appTemplates = getAppTemplates(projectDir, appDir); - Object.keys(appTemplates).forEach(function(templateName) { - const templateDestination = appTemplates[templateName]; - copyTemplate(templateName, templateDestination) - }); } -function removeProjectFiles(projectDir, appDir) { +function removeProjectFiles(projectDir) { const projectTemplates = getProjectTemplates(projectDir); Object.keys(projectTemplates).forEach(function(templateName) { const templateDestination = projectTemplates[templateName]; deleteFile(templateDestination); }); - - const appTemplates = getAppTemplates(projectDir, appDir); - Object.keys(appTemplates).forEach(function(templateName) { - const templateDestination = appTemplates[templateName]; - deleteFile(templateDestination); - }); } -function forceUpdateProjectFiles(projectDir, appDir) { - removeProjectFiles(projectDir, appDir); - addProjectFiles(projectDir, appDir); +function forceUpdateProjectFiles(projectDir) { + removeProjectFiles(projectDir); + addProjectFiles(projectDir); } function compareProjectFiles(projectDir) { @@ -69,31 +57,21 @@ function copyTemplate(templateName, destinationPath) { } function getProjectTemplates(projectDir) { - let templates = {} + const templates = {} + const WEBPACK_CONFIG_NAME = "webpack.config.js"; if (isAngular({projectDir})) { - templates["webpack.angular.js"] = "webpack.config.js"; + templates["webpack.angular.js"] = WEBPACK_CONFIG_NAME; + templates["tsconfig.esm.json"] = "tsconfig.esm.json"; } else if (isTypeScript({projectDir})) { - templates["webpack.typescript.js"] = "webpack.config.js"; + templates["webpack.typescript.js"] = WEBPACK_CONFIG_NAME; } else { - templates["webpack.javascript.js"] = "webpack.config.js"; + templates["webpack.javascript.js"] = WEBPACK_CONFIG_NAME; } return getFullTemplatesPath(projectDir, templates); } -function getAppTemplates(projectDir, appDir) { - const templates = {}; - - if (isAngular({projectDir})) { - templates["vendor.angular.ts"] = tsOrJs(projectDir, "vendor"); - } else { - templates["vendor.nativescript.ts"] = tsOrJs(projectDir, "vendor"); - } - - return getFullTemplatesPath(appDir, templates); -} - function getFullTemplatesPath(projectDir, templates) { let updatedTemplates = {}; @@ -111,15 +89,9 @@ function getFullPath(projectDir, filePath) { return path.resolve(projectDir, filePath); } -function tsOrJs(projectDir, name) { - const extension = isTypeScript({projectDir}) ? "ts" : "js"; - return `${name}.${extension}`; -} - module.exports = { addProjectFiles, removeProjectFiles, forceUpdateProjectFiles, compareProjectFiles, }; - diff --git a/projectHelpers.js b/projectHelpers.js index 7ebb1652..2984491c 100644 --- a/projectHelpers.js +++ b/projectHelpers.js @@ -4,11 +4,10 @@ const semver = require("semver"); const { EOL } = require("os"); const hook = require("nativescript-hook")(__dirname); -const { - PROJECT_DATA_GETTERS, - getProjectData, - safeGet, -} = require("./nsCliHelpers"); +const PROJECT_DATA_GETTERS = { + appPath: "getAppDirectoryRelativePath", + appResourcesPath: "getAppResourcesRelativeDirectoryPath", +}; const APP_DIR = "app"; const ANDROID_PROJECT_PATH = "platforms/android"; @@ -150,16 +149,6 @@ const getPackageJsonPath = projectDir => path.resolve(projectDir, "package.json" const isAndroid = platform => /android/i.test(platform); const isIos = platform => /ios/i.test(platform); -function getAppPath() { - const projectDir = getProjectDir(); - const projectData = getProjectData(projectDir); - const appDir = getAppPathFromProjectData(projectData) || APP_DIR; - - const appPath = path.resolve(projectDir, appDir); - - return appPath; -} - function getAppPathFromProjectData(data) { return safeGet(data, PROJECT_DATA_GETTERS.appPath); } @@ -168,9 +157,23 @@ function getAppResourcesPathFromProjectData(data) { return safeGet(data, PROJECT_DATA_GETTERS.appResourcesPath); } +function safeGet(object, property, ...args) { + if (!object) { + return; + } + + const value = object[property]; + if (!value) { + return; + } + + return typeof value === "function" ? + value.bind(object)(...args) : + value; +} + module.exports = { APP_DIR, - getAppPath, getAppPathFromProjectData, getAppResourcesPathFromProjectData, getAndroidProjectPath, diff --git a/register-modules.js b/register-modules.js new file mode 100644 index 00000000..c5b66929 --- /dev/null +++ b/register-modules.js @@ -0,0 +1,4 @@ +require("tns-core-modules/bundle-entry-points"); +const context = require.context("~/", true, /(root|page|fragment)\.(xml|css|js|ts|scss)$/); +global.registerWebpackModules(context); + diff --git a/templates/tsconfig.esm.json b/templates/tsconfig.esm.json new file mode 100644 index 00000000..95f2ecee --- /dev/null +++ b/templates/tsconfig.esm.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "module": "es2015", + "moduleResolution": "node" + } +} diff --git a/templates/vendor.angular.ts b/templates/vendor.angular.ts deleted file mode 100644 index fd29170f..00000000 --- a/templates/vendor.angular.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Snapshot the ~/app.css and the theme -const application = require("application"); -require("ui/styling/style-scope"); -const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/); -global.registerWebpackModules(appCssContext); -application.loadAppCss(); - -require("reflect-metadata"); -require("@angular/platform-browser"); -require("@angular/core"); -require("@angular/common"); -require("@angular/forms"); -require("@angular/http"); -require("@angular/router"); - -require("nativescript-angular/platform-static"); -require("nativescript-angular/forms"); -require("nativescript-angular/router"); diff --git a/templates/webpack.angular.js b/templates/webpack.angular.js index 397281f4..b7b366d0 100644 --- a/templates/webpack.angular.js +++ b/templates/webpack.angular.js @@ -37,13 +37,12 @@ module.exports = env => { appResourcesPath = "app/App_Resources", // You can provide the following flags when running 'tns run android|ios' - production, // --env.production aot, // --env.aot snapshot, // --env.snapshot uglify, // --env.uglify report, // --env.report } = env; - const ngToolsWebpackOptions = { tsConfigPath: join(__dirname, "tsconfig.json") }; + const ngToolsWebpackOptions = { tsConfigPath: join(__dirname, "tsconfig.esm.json") }; const appFullPath = resolve(projectRoot, appPath); const appResourcesFullPath = resolve(projectRoot, appResourcesPath); @@ -52,10 +51,9 @@ module.exports = env => { nsWebpack.getAotEntryModule(appFullPath) : `${nsWebpack.getEntryModule(appFullPath)}.ts`; const entryPath = `.${sep}${entryModule}`; - const vendorPath = `.${sep}vendor.ts`; const config = { - mode: production ? "production" : "development", + mode: uglify ? "production" : "development", context: appFullPath, watchOptions: { ignored: [ @@ -67,7 +65,6 @@ module.exports = env => { target: nativescriptTarget, entry: { bundle: entryPath, - vendor: vendorPath, }, output: { pathinfo: false, @@ -103,17 +100,15 @@ module.exports = env => { }, devtool: "none", optimization: { - runtimeChunk: { name: "vendor" }, splitChunks: { cacheGroups: { - common: { - name: "common", + vendor: { + name: "vendor", chunks: "all", test: (module, chunks) => { const moduleName = module.nameForCondition ? module.nameForCondition() : ''; return /[\\/]node_modules[\\/]/.test(moduleName) || appComponents.some(comp => comp === moduleName); - }, enforce: true, }, @@ -121,16 +116,43 @@ module.exports = env => { }, minimize: !!uglify, minimizer: [ - // Override default minimizer to work around an Android issue by setting compress = false new UglifyJsPlugin({ uglifyOptions: { - compress: platform !== "android" + parallel: true, + cache: true, + output: { + comments: false, + }, + compress: { + // The Android SBG has problems parsing the output + // when these options are enabled + 'collapse_vars': platform !== "android", + sequences: platform !== "android", + } } }) ], }, module: { rules: [ + { + test: new RegExp(entryPath), + use: [ + // Require all Android app components + platform === "android" && { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + }, + + { + loader: "nativescript-dev-webpack/bundle-config-loader", + options: { + loadCss: !snapshot, // load the application css if in debug mode + } + }, + ].filter(loader => !!loader) + }, + { test: /\.html$|\.xml$/, use: "raw-loader" }, // tns-core-modules reads the app.css and its imports using css-loader @@ -160,6 +182,13 @@ module.exports = env => { { loader: "@ngtools/webpack", options: ngToolsWebpackOptions }, ] }, + + // Mark files inside `@angular/core` as using SystemJS style dynamic imports. + // Removing this will cause deprecation warnings to appear. + { + test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/, + parser: { system: true }, + }, ], }, plugins: [ @@ -187,7 +216,6 @@ 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 @@ -208,18 +236,6 @@ module.exports = env => { ], }; - if (platform === "android") { - // 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({ @@ -233,11 +249,18 @@ module.exports = env => { if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ - chunks: [ "vendor", "common" ], + chunk: "vendor", + requireModules: [ + "reflect-metadata", + "@angular/platform-browser", + "@angular/core", + "@angular/common", + "@angular/router", + "nativescript-angular/platform-static", + "nativescript-angular/router", + ], projectRoot, webpackConfig: config, - targetArchs: ["arm", "arm64", "ia32"], - useLibs: false })); } diff --git a/templates/webpack.javascript.js b/templates/webpack.javascript.js index 0f5b612e..88e77ea4 100644 --- a/templates/webpack.javascript.js +++ b/templates/webpack.javascript.js @@ -37,7 +37,6 @@ module.exports = env => { appResourcesPath = "app/App_Resources", // You can provide the following flags when running 'tns run android|ios' - production, // --env.production snapshot, // --env.snapshot uglify, // --env.uglify report, // --env.report @@ -48,10 +47,9 @@ module.exports = env => { const entryModule = nsWebpack.getEntryModule(appFullPath); const entryPath = `.${sep}${entryModule}.js`; - const vendorPath = `.${sep}vendor.js`; const config = { - mode: production ? "production" : "development", + mode: uglify ? "production" : "development", context: appFullPath, watchOptions: { ignored: [ @@ -63,7 +61,6 @@ module.exports = env => { target: nativescriptTarget, entry: { bundle: entryPath, - vendor: vendorPath, }, output: { pathinfo: false, @@ -99,11 +96,10 @@ module.exports = env => { }, devtool: "none", optimization: { - runtimeChunk: { name: "vendor" }, splitChunks: { cacheGroups: { - common: { - name: "common", + vendor: { + name: "vendor", chunks: "all", test: (module, chunks) => { const moduleName = module.nameForCondition ? module.nameForCondition() : ''; @@ -117,16 +113,44 @@ module.exports = env => { }, minimize: !!uglify, minimizer: [ - // Override default minimizer to work around an Android issue by setting compress = false new UglifyJsPlugin({ uglifyOptions: { - compress: platform !== "android" + parallel: true, + cache: true, + output: { + comments: false, + }, + compress: { + // The Android SBG has problems parsing the output + // when these options are enabled + 'collapse_vars': platform !== "android", + sequences: platform !== "android", + } } }) ], }, module: { rules: [ + { + test: new RegExp(entryPath), + use: [ + // Require all Android app components + platform === "android" && { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + }, + + { + loader: "nativescript-dev-webpack/bundle-config-loader", + options: { + registerPages: true, // applicable only for non-angular apps + loadCss: !snapshot, // load the application css if in debug mode + } + }, + ].filter(loader => !!loader) + }, + { test: /\.(html|xml)$/, use: "raw-loader" }, { @@ -167,9 +191,8 @@ module.exports = env => { ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ - "./vendor", // install webpackJsonpCallback - "./common", // require app/vendor.js - "./bundle", // require the entry module (app/app.js) + "./vendor", + "./bundle", ]), // Support for web workers since v3.2 new NativeScriptWorkerPlugin(), @@ -182,18 +205,6 @@ module.exports = env => { ], }; - if (platform === "android") { - // 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({ @@ -207,14 +218,12 @@ module.exports = env => { if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ - chunks: [ - "common", - "vendor", + chunk: "vendor", + requireModules: [ + "tns-core-modules/bundle-entry-points", ], projectRoot, webpackConfig: config, - targetArchs: ["arm", "arm64", "ia32"], - useLibs: false })); } diff --git a/templates/webpack.typescript.js b/templates/webpack.typescript.js index 910da06c..b42b79de 100644 --- a/templates/webpack.typescript.js +++ b/templates/webpack.typescript.js @@ -37,7 +37,6 @@ module.exports = env => { appResourcesPath = "app/App_Resources", // You can provide the following flags when running 'tns run android|ios' - production, // --env.production snapshot, // --env.snapshot uglify, // --env.uglify report, // --env.report @@ -48,10 +47,9 @@ module.exports = env => { const entryModule = nsWebpack.getEntryModule(appFullPath); const entryPath = `.${sep}${entryModule}.ts`; - const vendorPath = `.${sep}vendor.ts`; const config = { - mode: production ? "production" : "development", + mode: uglify ? "production" : "development", context: appFullPath, watchOptions: { ignored: [ @@ -63,7 +61,6 @@ module.exports = env => { target: nativescriptTarget, entry: { bundle: entryPath, - vendor: vendorPath, }, output: { pathinfo: false, @@ -99,11 +96,10 @@ module.exports = env => { }, devtool: "none", optimization: { - runtimeChunk: { name: "vendor" }, splitChunks: { cacheGroups: { - common: { - name: "common", + vendor: { + name: "vendor", chunks: "all", test: (module, chunks) => { const moduleName = module.nameForCondition ? module.nameForCondition() : ''; @@ -117,16 +113,44 @@ module.exports = env => { }, minimize: !!uglify, minimizer: [ - // Override default minimizer to work around an Android issue by setting compress = false new UglifyJsPlugin({ uglifyOptions: { - compress: platform !== "android" + parallel: true, + cache: true, + output: { + comments: false, + }, + compress: { + // The Android SBG has problems parsing the output + // when these options are enabled + 'collapse_vars': platform !== "android", + sequences: platform !== "android", + } } }) ], }, module: { rules: [ + { + test: new RegExp(entryPath), + use: [ + // Require all Android app components + platform === "android" && { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + }, + + { + loader: "nativescript-dev-webpack/bundle-config-loader", + options: { + registerPages: true, // applicable only for non-angular apps + loadCss: !snapshot, // load the application css if in debug mode + } + }, + ].filter(loader => !!loader) + }, + { test: /\.(html|xml)$/, use: "raw-loader" }, { @@ -169,9 +193,8 @@ module.exports = env => { ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ - "./vendor", // install webpackJsonpCallback - "./common", // require app/vendor.js - "./bundle", // require the entry module (app/app.js) + "./vendor", + "./bundle", ]), // Support for web workers since v3.2 new NativeScriptWorkerPlugin(), @@ -184,18 +207,6 @@ module.exports = env => { ], }; - if (platform === "android") { - // 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({ @@ -209,14 +220,12 @@ module.exports = env => { if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ - chunks: [ - "common", - "vendor", + chunk: "vendor", + requireModules: [ + "tns-core-modules/bundle-entry-points", ], projectRoot, webpackConfig: config, - targetArchs: ["arm", "arm64", "ia32"], - useLibs: false })); } diff --git a/verify/update.js b/verify/update.js index fa37c172..1484917f 100644 --- a/verify/update.js +++ b/verify/update.js @@ -2,7 +2,6 @@ const { spawn } = require("child_process"); const { resolve: pathResolve } = require("path"); const { - getAppPath, getPackageJson, getProjectDir, writePackageJson, @@ -48,9 +47,7 @@ function updateDeps(projectDir) { function updateConfigs(projectDir) { console.info("Updating configuration files..."); - - const appDir = getAppPath(); - forceUpdateProjectFiles(projectDir, appDir); + forceUpdateProjectFiles(projectDir); } function execute(command) {