diff --git a/.gitignore b/.gitignore index a49ae8ad..251f193f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,9 @@ plugins/WatchStateLoggerPlugin.d.ts plugins/WatchStateLoggerPlugin.js plugins/WatchStateLoggerPlugin.js.map +host/platform.d.ts +host/platform.js +host/platform.js.map + hooks .DS_Store diff --git a/bundle-config-loader.js b/bundle-config-loader.js index 3d1b86a8..fdcf668f 100644 --- a/bundle-config-loader.js +++ b/bundle-config-loader.js @@ -1,8 +1,8 @@ module.exports = function(source) { this.cacheable(); - const { registerPages = true, loadCss = true } = this.query; + const { angular = false, loadCss = true } = this.query; - if (registerPages) { + if (!angular) { source = ` require("nativescript-dev-webpack/register-modules"); ${source} @@ -11,7 +11,7 @@ module.exports = function(source) { if (loadCss) { source = ` - require("nativescript-dev-webpack/load-application-css"); + require("nativescript-dev-webpack/load-application-css")(${angular}); ${source} `; } diff --git a/demo/AngularApp/package.json b/demo/AngularApp/package.json index 052fc6a0..317d60ed 100644 --- a/demo/AngularApp/package.json +++ b/demo/AngularApp/package.json @@ -13,15 +13,15 @@ } }, "dependencies": { - "@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", + "@angular/common": "~6.0.0", + "@angular/compiler": "~6.0.0", + "@angular/core": "~6.0.0", + "@angular/forms": "~6.0.0", + "@angular/http": "~6.0.0", + "@angular/platform-browser": "~6.0.0", + "@angular/platform-browser-dynamic": "~6.0.0", + "@angular/router": "~6.0.0", + "nativescript-angular": "~6.0.0", "nativescript-theme-core": "~1.0.2", "reflect-metadata": "~0.1.8", "rxjs": "~6.0.0-beta.1", @@ -29,9 +29,9 @@ "zone.js": "^0.8.4" }, "devDependencies": { - "@angular-devkit/core": "~0.5.5", - "@angular/compiler-cli": "~6.0.0-rc.0", - "@ngtools/webpack": "~6.0.0-rc.3", + "@angular-devkit/core": "~0.7.0-beta.1", + "@angular/compiler-cli": "~6.0.0", + "@ngtools/webpack": "~6.1.0-beta.1", "@types/chai": "^4.0.2", "@types/mocha": "^2.2.41", "@types/node": "^7.0.5", @@ -42,7 +42,7 @@ "chai-as-promised": "~7.1.1", "clean-webpack-plugin": "~0.1.19", "copy-webpack-plugin": "~4.5.1", - "css-loader": "~0.28.7", + "css-loader": "~0.28.11", "extract-text-webpack-plugin": "~3.0.2", "lazy": "1.0.11", "mocha": "~3.5.0", @@ -52,15 +52,15 @@ "nativescript-dev-sass": "^1.3.5", "nativescript-dev-typescript": "next", "nativescript-dev-webpack": "file:../..", - "nativescript-worker-loader": "~0.8.1", + "nativescript-worker-loader": "~0.9.0", "raw-loader": "~0.5.1", "resolve-url-loader": "~2.3.0", - "sass-loader": "~6.0.6", + "sass-loader": "~7.0.1", "typescript": "~2.7.2", - "uglifyjs-webpack-plugin": "~1.2.4", - "webpack": "~4.5.0", - "webpack-bundle-analyzer": "^2.9.1", - "webpack-cli": "~2.0.14", + "uglifyjs-webpack-plugin": "~1.2.5", + "webpack": "~4.6.0", + "webpack-bundle-analyzer": "~2.13.0", + "webpack-cli": "~2.1.3", "webpack-sources": "~1.1.0" }, "scripts": { diff --git a/dependencyManager.js b/dependencyManager.js index e15c1366..eb15c164 100644 --- a/dependencyManager.js +++ b/dependencyManager.js @@ -74,8 +74,8 @@ function getRequiredDeps(packageJson) { if (isAngular({packageJson})) { Object.assign(deps, { "@angular/compiler-cli": packageJson.dependencies["@angular/core"], - "@ngtools/webpack": "~6.0.3", - "@angular-devkit/core": "~0.6.3", + "@ngtools/webpack": "~6.1.0-beta.1", + "@angular-devkit/core": "~0.7.0-beta.1", "resolve-url-loader": "~2.3.0", }); } else if (isTypeScript({packageJson})) { diff --git a/host/platform.ts b/host/platform.ts new file mode 100644 index 00000000..9af7eeb7 --- /dev/null +++ b/host/platform.ts @@ -0,0 +1,97 @@ +import { + parse, + join, +} from "path"; +import { statSync } from "fs"; + +import { Observable } from "rxjs"; +import { + Path, + PathFragment, +} from "@angular-devkit/core"; +import { + FileBuffer, + Host, + HostCapabilities, + HostWatchOptions, + HostWatchEvent, + Stats, +} from "@angular-devkit/core/src/virtual-fs/host"; +const { NodeJsSyncHost } = require("@angular-devkit/core/node"); + + +export class PlatformReplacementHost implements Host { + constructor(protected _platforms: string[], protected _delegate = new NodeJsSyncHost()) { + } + + protected _resolve(path) { + const { dir, name, ext } = parse(path); + + for (const platform of this._platforms) { + const platformFileName = `${name}.${platform}${ext}`; + const platformPath = this.toSystemPath(join(dir, platformFileName)); + + try { + const stat = statSync(platformPath); + if (stat && stat.isFile()) { + return platformPath; + } + } catch(_e) { + // continue checking the other platforms + } + } + + return path; + } + + // Convert paths from \c\some\path to c:\some\path + private toSystemPath(path: string) { + if (!process.platform.startsWith("win32")) { + return path; + } + + const drive = path.match(/^\\(\w)\\(.*)$/); + return drive ? + `${drive[1]}:\\${drive[2]}`: + path; + } + + get capabilities(): HostCapabilities { + return this._delegate.capabilities; + } + + write(path: Path, content: FileBuffer): Observable { + return this._delegate.write(this._resolve(path), content); + } + read(path: Path): Observable { + return this._delegate.read(this._resolve(path)); + } + delete(path: Path): Observable { + return this._delegate.delete(this._resolve(path)); + } + rename(from: Path, to: Path): Observable { + return this._delegate.rename(this._resolve(from), this._resolve(to)); + } + + list(path: Path): Observable { + return this._delegate.list(this._resolve(path)); + } + + exists(path: Path): Observable { + return this._delegate.exists(this._resolve(path)); + } + isDirectory(path: Path): Observable { + return this._delegate.isDirectory(this._resolve(path)); + } + isFile(path: Path): Observable { + return this._delegate.isFile(this._resolve(path)); + } + + stat(path: Path): Observable> | null { + return this._delegate.stat(this._resolve(path)); + } + + watch(path: Path, options?: HostWatchOptions): Observable | null { + return this._delegate.watch(this._resolve(path), options); + } +} diff --git a/index.js b/index.js index d11a6317..17657000 100644 --- a/index.js +++ b/index.js @@ -4,19 +4,12 @@ const { existsSync } = require("fs"); const { ANDROID_APP_PATH } = require("./androidProjectHelpers"); const { getPackageJson, - isAngular, isAndroid, isIos, } = require("./projectHelpers"); Object.assign(exports, require('./plugins')); -exports.loadAdditionalPlugins = function (projectSettings) { - if (isAngular(projectSettings)) { - Object.assign(exports, require('./plugins/angular')(projectSettings.projectDir)); - } -} - exports.getAotEntryModule = function (appDirectory) { verifyEntryModuleDirectory(appDirectory); diff --git a/load-application-css.js b/load-application-css.js index bc8b8fb9..fde6ba41 100644 --- a/load-application-css.js +++ b/load-application-css.js @@ -1,6 +1,14 @@ -const application = require("application"); -require("ui/styling/style-scope"); -const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/); -global.registerWebpackModules(appCssContext); -application.loadAppCss(); +module.exports = function(isAngular) { + const application = require("tns-core-modules/application"); + require("tns-core-modules/ui/styling/style-scope"); + + if (isAngular) { + global.registerModule("./app.css", () => require("~/app")); + } else { + const appCssContext = require.context("~/", false, /^\.\/app\.(css|scss|less|sass)$/); + global.registerWebpackModules(appCssContext); + } + + application.loadAppCss(); +} diff --git a/package.json b/package.json index 32a05031..41ee65a1 100644 --- a/package.json +++ b/package.json @@ -77,9 +77,10 @@ "tapable": "1.0.0" }, "devDependencies": { - "@ngtools/webpack": "~6.0.3", + "@angular-devkit/core": "^0.7.0-beta.1", "@types/node": "^8.0.0", "conventional-changelog-cli": "^1.3.22", + "rxjs": "^6.2.0", "source-map-support": "^0.5.0", "typescript": "~2.7.2" } diff --git a/plugins/NativeScriptAngularCompilerPlugin.ts b/plugins/NativeScriptAngularCompilerPlugin.ts deleted file mode 100644 index fee60125..00000000 --- a/plugins/NativeScriptAngularCompilerPlugin.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as path from "path"; -import { PlatformFSPlugin, PlatformFSPluginOptions, mapFileSystem } from "./PlatformFSPlugin"; -import * as ngToolsWebpack from "@ngtools/webpack"; -module.exports = (projectDir) => { - // During development the nativescript-dev-webpack plugin may have @ngtools/webpack installed locally as dev-dependency, - // we want to make sure we are using the one installed in the actual app - const ngToolsWebpackDir = path.join(projectDir, "node_modules", "@ngtools", "webpack"); - const appNgToolsWebpack: typeof ngToolsWebpack = require(ngToolsWebpackDir); - const AngularCompilerPlugin: typeof ngToolsWebpack.AngularCompilerPlugin = appNgToolsWebpack.AngularCompilerPlugin; - - class NativeScriptAngularCompilerPlugin extends AngularCompilerPlugin { - readonly options: NativeScriptAngularCompilerPluginOptions; - readonly platform: string; - - get __compilerHost() { - // Accessing private API of the AngularCompilerPlugin - // We need this to augment at least the "resourceNameToFileName" so we can map - // component.css to component.android.css etc. for platform specific css and html resources. - return (this)._compilerHost; - } - - constructor(options: NativeScriptAngularCompilerPluginOptions) { - super(options); - - this.platform = (this.options.platformOptions && this.options.platformOptions.platform) || undefined; - const platform = this.platform; - - if (platform) { - // https://github.com/angular/angular/blob/7bfeac746e717d02e062fe4a65c008060b8b662c/packages/compiler-cli/src/transformers/api.ts - const resourceNameToFileName = this.__compilerHost.resourceNameToFileName || function (file, relativeTo) { - const resolved = path.resolve(path.dirname(relativeTo), file); - if (this.fileExists(resolved)) { - return resolved; - } else { - return null; - } - }; - this.__compilerHost.resourceNameToFileName = function (file, relativeTo) { - const parsed = path.parse(file); - const platformFile = parsed.name + "." + platform + parsed.ext; - let resolved; - try { - resolved = resourceNameToFileName.call(this, platformFile, relativeTo); - } catch (e) { - } - resolved = resolved || resourceNameToFileName.call(this, file, relativeTo); - resolved = resolved && resolved.replace(/\\/g, "/"); - return resolved; - }; - } - } - - getCompiledFile(this: NativeScriptAngularCompilerPlugin, file: string): CompiledFile { - try { - if (this.platform) { - const parsed = path.parse(file); - const platformFile = parsed.dir + path.sep + parsed.name + "." + this.platform + parsed.ext; - const result = super.getCompiledFile(platformFile); - return result; - } - } catch (e) { - } - return super.getCompiledFile(file); - } - - apply(compiler) { - super.apply(compiler); - if (this.options.platformOptions && this.options.platformOptions.platform && this.options.platformOptions.platforms) { - compiler.hooks.environment.tap("NativeScriptAngularCompilerPlugin", () => { - compiler.inputFileSystem = mapFileSystem({ - fs: compiler.inputFileSystem, - context: compiler.context, - platform: this.options.platformOptions.platform, - platforms: this.options.platformOptions.platforms, - ignore: this.options.platformOptions.ignore - }); - - compiler.watchFileSystem = mapFileSystem({ - fs: compiler.watchFileSystem, - context: compiler.context, - platform: this.options.platformOptions.platform, - platforms: this.options.platformOptions.platforms, - ignore: this.options.platformOptions.ignore - }); - }); - } - } - } - - return { - AngularCompilerPlugin, - NativeScriptAngularCompilerPlugin - }; -} - -export interface NativeScriptAngularCompilerPluginOptions extends ngToolsWebpack.AngularCompilerPluginOptions { - platformOptions?: PlatformFSPluginOptions; -} - -export interface CompiledFile { - outputText: string; - sourceMap: string; - errorDependencies: string[]; -} diff --git a/plugins/NativeScriptSnapshotPlugin/index.js b/plugins/NativeScriptSnapshotPlugin/index.js index 747764ad..469fb3b5 100644 --- a/plugins/NativeScriptSnapshotPlugin/index.js +++ b/plugins/NativeScriptSnapshotPlugin/index.js @@ -45,7 +45,7 @@ exports.NativeScriptSnapshotPlugin = (function() { let snapshotEntryContent = ""; if (includeApplicationCss) { - snapshotEntryContent += `require("nativescript-dev-webpack/load-application-css");`; + snapshotEntryContent += `require("nativescript-dev-webpack/load-application-css")(${options.angular});`; } snapshotEntryContent += [ ...requireModules, ...internalRequireModules] .map(mod => `require('${mod}')`).join(";"); @@ -155,3 +155,4 @@ exports.NativeScriptSnapshotPlugin = (function() { return NativeScriptSnapshotPlugin; })(); + diff --git a/plugins/NativeScriptSnapshotPlugin/options.json b/plugins/NativeScriptSnapshotPlugin/options.json index 3cf3b52d..513afd4e 100644 --- a/plugins/NativeScriptSnapshotPlugin/options.json +++ b/plugins/NativeScriptSnapshotPlugin/options.json @@ -4,6 +4,10 @@ "androidNdkPath": { "type": "string" }, + "angular": { + "type": "boolean", + "default": false + }, "chunk": { "type": "string" }, diff --git a/plugins/angular.js b/plugins/angular.js deleted file mode 100644 index ddfe1288..00000000 --- a/plugins/angular.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (projectDir) => Object.assign({}, - require("./NativeScriptAngularCompilerPlugin")(projectDir) -); diff --git a/templates/webpack.angular.js b/templates/webpack.angular.js index 8b729ef5..97c3beb2 100644 --- a/templates/webpack.angular.js +++ b/templates/webpack.angular.js @@ -3,11 +3,13 @@ const { join, relative, resolve, sep } = require("path"); const webpack = require("webpack"); const nsWebpack = require("nativescript-dev-webpack"); const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); +const { PlatformReplacementHost } = require("nativescript-dev-webpack/host/platform"); const CleanWebpackPlugin = require("clean-webpack-plugin"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); +const { AngularCompilerPlugin } = require("@ngtools/webpack"); module.exports = env => { // Add your custom Activities, Services and other Android app components here. @@ -21,9 +23,10 @@ module.exports = env => { throw new Error("You need to provide a target platform!"); } - const platforms = ["ios", "android"]; + const extensions = ["tns", platform]; + const platformHost = new PlatformReplacementHost(extensions); + const projectRoot = __dirname; - nsWebpack.loadAdditionalPlugins({ projectDir: projectRoot }); // Default destination inside platforms//... const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); @@ -146,7 +149,7 @@ module.exports = env => { { loader: "nativescript-dev-webpack/bundle-config-loader", options: { - registerPages: false, + angular: true, loadCss: !snapshot, // load the application css if in debug mode } }, @@ -220,15 +223,12 @@ module.exports = env => { // For instructions on how to set up workers with webpack // check out https://github.com/nativescript/worker-loader new NativeScriptWorkerPlugin(), - // AngularCompilerPlugin with augmented NativeScript filesystem to handle platform specific resource resolution. - new nsWebpack.NativeScriptAngularCompilerPlugin({ + + new AngularCompilerPlugin({ + host: platformHost, entryModule: resolve(appPath, "app.module#AppModule"), tsConfigPath: join(__dirname, "tsconfig.esm.json"), skipCodeGeneration: !aot, - platformOptions: { - platform, - platforms, - }, }), // Does IPC communication with the {N} CLI to notify events when running in watch mode. new nsWebpack.WatchStateLoggerPlugin(), @@ -249,6 +249,7 @@ module.exports = env => { if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ chunk: "vendor", + angular: true, requireModules: [ "reflect-metadata", "@angular/platform-browser", diff --git a/templates/webpack.javascript.js b/templates/webpack.javascript.js index 0ddb0af4..1700a92f 100644 --- a/templates/webpack.javascript.js +++ b/templates/webpack.javascript.js @@ -23,7 +23,6 @@ module.exports = env => { const platforms = ["ios", "android"]; const projectRoot = __dirname; - nsWebpack.loadAdditionalPlugins({ projectDir: projectRoot }); // Default destination inside platforms//... const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); @@ -144,7 +143,6 @@ module.exports = env => { { 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 } }, diff --git a/templates/webpack.typescript.js b/templates/webpack.typescript.js index aaeb5054..7394c84d 100644 --- a/templates/webpack.typescript.js +++ b/templates/webpack.typescript.js @@ -23,7 +23,6 @@ module.exports = env => { const platforms = ["ios", "android"]; const projectRoot = __dirname; - nsWebpack.loadAdditionalPlugins({ projectDir: projectRoot }); // Default destination inside platforms//... const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); @@ -146,7 +145,6 @@ module.exports = env => { { 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 } }, diff --git a/tsconfig.json b/tsconfig.json index 2d71e264..fe233eaf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,8 +8,8 @@ "skipLibCheck": true }, "files": [ - "plugins/NativeScriptAngularCompilerPlugin.ts", "plugins/PlatformFSPlugin.ts", - "plugins/WatchStateLoggerPlugin.ts" + "plugins/WatchStateLoggerPlugin.ts", + "host/platform.ts" ] -} \ No newline at end of file +}