diff --git a/CHANGELOG.md b/CHANGELOG.md index 304ac1af..f0a5941b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ + +# [0.4.0](https://github.com/NativeScript/nativescript-dev-webpack/compare/v0.3.7...v0.4.0) (2017-04-20) + + +### Bug Fixes + +* add webpack.common template for JS projects ([7451545](https://github.com/NativeScript/nativescript-dev-webpack/commit/7451545)), closes [#113](https://github.com/NativeScript/nativescript-dev-webpack/issues/113) +* **installer:** show helper message for new dependencies ([#122](https://github.com/NativeScript/nativescript-dev-webpack/issues/122)) ([5c7ebeb](https://github.com/NativeScript/nativescript-dev-webpack/commit/5c7ebeb)) +* **ns-bundle:** use remove/add platform instead of clean-app ([#116](https://github.com/NativeScript/nativescript-dev-webpack/issues/116)) ([6609370](https://github.com/NativeScript/nativescript-dev-webpack/commit/6609370)) +* **tsconfig:** add "exclude" property to aot config ([#120](https://github.com/NativeScript/nativescript-dev-webpack/issues/120)) ([d28dba1](https://github.com/NativeScript/nativescript-dev-webpack/commit/d28dba1)), closes [#101](https://github.com/NativeScript/nativescript-dev-webpack/issues/101) + + +### Features + +* detect required devDeps versions ([9b102c3](https://github.com/NativeScript/nativescript-dev-webpack/commit/9b102c3)) + + + ## [0.3.7](https://github.com/NativeScript/nativescript-dev-webpack/compare/v0.3.6...v0.3.7) (2017-03-31) diff --git a/README.md b/README.md index 49dc18a7..530d6b48 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,67 @@ A package to help with webpacking NativeScript apps. * webpack config templates. * helper functions that place files at the correct locations before packaging apps. -* loaders and plugins for vanilla NativeScript and Angular 2 apps. +* loaders and plugins for vanilla NativeScript and Angular apps. # Usage ```sh $ npm install --save-dev nativescript-dev-webpack +$ npm install + $ npm run start-android-bundle +or +$ npm run start-ios-bundle ``` # Documentation For details, see the [NativeScript docs](http://docs.nativescript.org/angular/tooling/bundling-with-webpack.html). + +# Note about dependencies. + +The `nativescript-dev-webpack` plugin adds a few devDependencies to the project. Make sure to run either `npm install` or `yarn` after installing the plugin. +The versions of the newly added packages depend on the versions of some of your already added packages. More specifically - `tns-core-modules` and all packages from the `@angular` scope. Since version 0.4.0, nativescript-dev-webpack will automatically add the correct development dependencies, based on what you already have installed. +You can force update your `package.json` using the `update-ns-webpack` script which you can find in `PROJECT_DIR/node_modules/.bin`. +If the bundling process fails, please make sure you have the correct versions of the packages. + +If you are targetting: +``` + "tns-core-modules": "^3.0.0" or "^3.0.0-rc.1" or "rc" + "@angular/": "^4.0.0" +``` +Then you should use: +``` + "nativescript-angular": "rc" or "internal-preview" + "@ngtools/webpack": "1.3.0" + "typescript": "~2.2.2" +``` + + +If you are targetting: +``` + "tns-core-modules": "~2.5.0" + "@angular/": "^4.0.0" +``` +Then you should use: +``` + "nativescript-angular": "~1.5.1" + "@ngtools/webpack": "1.2.13" + "typescript": "~2.1.6" +``` + + +If you are targetting: +``` + "tns-core-modules": "~2.5.0" + "@angular/": "~2.4.0" +``` +Then you should use: +``` + "nativescript-angular": "~1.4.1" + "@ngtools/webpack": "1.2.10" + "typescript": "~2.1.6" +``` + + +P.S. Also please make sure you are using the same version of all `@angular/` packages, including the devDependency of `@angular/compiler-cli`. \ No newline at end of file diff --git a/bin/install-ns-webpack b/bin/install-ns-webpack index 7b330b80..e8445188 100644 --- a/bin/install-ns-webpack +++ b/bin/install-ns-webpack @@ -1,7 +1,3 @@ #!/usr/bin/env node -var installer = require("../installer"); - -installer.addProjectFiles(); -installer.removeDeprecatedNpmScripts(); -installer.addNpmScripts(); -installer.addProjectDependencies(); +const installer = require("../installer"); +installer.install(); diff --git a/bin/ns-bundle b/bin/ns-bundle index 72008625..9e28ffd3 100644 --- a/bin/ns-bundle +++ b/bin/ns-bundle @@ -37,7 +37,11 @@ function execute(options) { ]; if (options.bundle) { - commands.unshift(() => webpack(options.platform)); + commands = [ + () => prepare(options.platform), + () => webpack(options.platform), + ...commands + ]; } return commands.reduce((current, next) => current.then(next), Promise.resolve()); @@ -47,17 +51,42 @@ function webpack(platform) { return new Promise(function (resolve, reject) { console.log(`Running webpack for ${platform}...`); - spawnChildProcess("tns", "clean-app", platform) - .then(() => spawnChildProcess("webpack", `--config=webpack.${platform}.js`, "--progress")) + spawnChildProcess(true, "webpack", `--config=webpack.${platform}.js`, "--progress") .then(resolve) .catch(throwError); }); } +function prepare(platform) { + return removePlatform(platform) + .then(() => addPlatform(platform)); +} + +function removePlatform(platform) { + return new Promise(function (resolve, reject) { + console.log(`Removing platform ${platform}...`); + + spawnChildProcess(false, "tns", "platform", "remove", platform) + .then(resolve) + .catch(resolve); + }); +} + +function addPlatform(platform) { + return new Promise(function (resolve, reject) { + console.log(`Adding platform ${platform}...`); + + spawnChildProcess(false, "tns", "platform", "add", platform) + .then(resolve) + .catch(resolve); + }); +} + function runTns(command, platform) { - console.log(`Running tns ${command}...`); return new Promise((resolve, reject) => { - spawnChildProcess("tns", command, platform, "--bundle", "--disable-npm-install", ...tnsArgs) + console.log(`Running tns ${command}...`); + + spawnChildProcess(true, "tns", command, platform, "--bundle", "--disable-npm-install", ...tnsArgs) .then(resolve) .catch(throwError); }); @@ -100,9 +129,11 @@ function getCommand(flags) { } } -function spawnChildProcess(command, ...args) { +function spawnChildProcess(shouldPrintOutput, command, ...args) { + const stdio = shouldPrintOutput ? "inherit" : "ignore"; + return new Promise((resolve, reject) => { - const childProcess = spawn(command, args, { stdio: "inherit", pwd: PROJECT_DIR, shell: true }); + const childProcess = spawn(command, args, { stdio, pwd: PROJECT_DIR, shell: true }); childProcess.on("close", (code) => { if (code === 0) { diff --git a/bin/ns-verify-bundle b/bin/ns-verify-bundle index c423e746..695767c4 100644 --- a/bin/ns-verify-bundle +++ b/bin/ns-verify-bundle @@ -4,7 +4,6 @@ const path = require("path"); const fs = require("fs"); const PROJECT_DIR = path.resolve(__dirname, "../../../"); -console.log(PROJECT_DIR); const APP_ID = require(path.resolve(PROJECT_DIR, "./package.json")).nativescript.id; const APP_NAME = APP_ID.substring(APP_ID.lastIndexOf(".") + 1); const PROJECT_PATHS = { diff --git a/bin/ns-webpack-update.cmd b/bin/ns-webpack-update.cmd new file mode 100644 index 00000000..135558e7 --- /dev/null +++ b/bin/ns-webpack-update.cmd @@ -0,0 +1 @@ +@node %~dp0\ns-webpack-update %* diff --git a/bin/remove-ns-webpack b/bin/remove-ns-webpack index b7677950..88ea2bfa 100644 --- a/bin/remove-ns-webpack +++ b/bin/remove-ns-webpack @@ -1,7 +1,3 @@ #!/usr/bin/env node -var installer = require("../installer"); - -installer.removeProjectFiles(); -installer.removeDeprecatedNpmScripts(); -installer.removeNpmScripts(); -installer.removeProjectDependencies(); +const installer = require("../installer"); +installer.uninstall(); diff --git a/bin/remove-ns-webpack.cmd b/bin/remove-ns-webpack.cmd index 5de779b8..311f4ecf 100644 --- a/bin/remove-ns-webpack.cmd +++ b/bin/remove-ns-webpack.cmd @@ -1 +1 @@ -@node %~dp0\install-ns-webpack %* +@node %~dp0\remove-ns-webpack %* diff --git a/bin/update-ns-webpack b/bin/update-ns-webpack new file mode 100644 index 00000000..6a2c47c8 --- /dev/null +++ b/bin/update-ns-webpack @@ -0,0 +1,13 @@ +#!/usr/bin/env node +const path = require("path"); +const fs = require("fs"); + +const helpers = require("../projectHelpers"); +const forceUpdateProjectDeps = require("../dependencyManager").forceUpdateProjectDeps; + +const PROJECT_DIR = path.resolve(__dirname, "../../../"); +const packageJson = helpers.getPackageJson(PROJECT_DIR); + +packageJson.devDependencies = forceUpdateProjectDeps(packageJson); + +helpers.writePackageJson(packageJson, PROJECT_DIR); diff --git a/dependencyManager.js b/dependencyManager.js new file mode 100644 index 00000000..7e455a9e --- /dev/null +++ b/dependencyManager.js @@ -0,0 +1,115 @@ +const helpers = require("./projectHelpers"); + +const NEW_DEPS_MESSAGE = ` +A few development dependencies were added. \ +Install them before bundling your project.`; + +const ALREADY_ADDED_MESSAGE = `\ +Some dependencies may have already been added. \ +If you want to force update them, please run "node_modules/.bin/update-ns-webpack".`; + +function forceUpdateProjectDeps(packageJson) { + return addProjectDeps(packageJson, true); +} + +function addProjectDeps(packageJson, force = false) { + const depsToAdd = getRequiredDeps(packageJson); + packageJson.devDependencies = packageJson.devDependencies || {}; + let deps = Object.assign({}, packageJson.devDependencies); + + Object.keys(depsToAdd).forEach(function(name) { + const version = depsToAdd[name]; + deps = addDependency(deps, name, version, force); + }); + + logHelperMessages(); + + return deps; +} + +function addDependency(deps, name, version, force) { + if (!deps[name] || force) { + deps[name] = version; + console.info(`Adding dev dependency: ${name}@${version}`); + } else if (deps[name] !== version) { + console.info(`Dev dependency: ${name} already added. Leaving version: ${deps[name]}`); + } + + return deps; +} + +function getRequiredDeps(packageJson) { + let deps = { + "webpack": "~2.3.3", + "webpack-sources": "~0.2.3", + "copy-webpack-plugin": "~4.0.1", + "raw-loader": "~0.5.1", + "nativescript-css-loader": "~0.26.0", + "resolve-url-loader": "~2.0.2", + "extract-text-webpack-plugin": "~2.1.0", + }; + + if (helpers.isAngular({packageJson})) { + const angularDeps = resolveAngularDeps(packageJson.dependencies); + deps = Object.assign(deps, angularDeps); + } else if (helpers.isTypeScript({packageJson})) { + deps["awesome-typescript-loader"] = "~3.1.2"; + } + + return deps; +} + +function resolveAngularDeps(usedDependencies) { + let depsToAdd = { + "@angular/compiler-cli": usedDependencies["@angular/core"], + }; + const tnsModulesVersion = getVersionWithoutPatch(usedDependencies["tns-core-modules"]); + const angularCoreVersion = getVersionWithoutPatch(usedDependencies["@angular/core"]); + + if (angularCoreVersion.startsWith("2.")) { + Object.assign(depsToAdd, { + "typescript": "~2.1.6", + "@ngtools/webpack": "1.2.10", + }); + } else if (tnsModulesVersion.startsWith("2.")) { + Object.assign(depsToAdd, { + "typescript": "~2.1.6", + "@ngtools/webpack": "1.2.13", + }); + } else { + Object.assign(depsToAdd, { + "typescript": "~2.2.2", + "@ngtools/webpack": "1.3.0", + }); + } + + return depsToAdd; +} + +function getVersionWithoutPatch(version) { + if (!version) { + return ""; + } + + if (version === "next" || version === "latest" || version === "rc") { + return version; + } + + version = version.substring(0, version.lastIndexOf(".")); + + if (version.startsWith("~") || version.startsWith("^")) { + return version.substring(1); + } else { + return version; + } +} + +function logHelperMessages(someAlreadyAdded) { + console.info(NEW_DEPS_MESSAGE); + console.info(ALREADY_ADDED_MESSAGE); +} + +module.exports = { + forceUpdateProjectDeps, + addProjectDeps, +}; diff --git a/installer.js b/installer.js index 725f0c32..9cb2e197 100644 --- a/installer.js +++ b/installer.js @@ -1,276 +1,43 @@ -var path = require("path"); -var fs = require("fs"); -var childProcess = require("child_process"); +const path = require("path"); +const fs = require("fs"); -var projectDir = path.dirname(path.dirname(__dirname)); -var appDir = path.join(projectDir, "app"); +const helpers = require("./projectHelpers"); +const projectFilesManager = require("./projectFilesManager"); +const npmScriptsManager = require("./npmScriptsManager"); +const dependencyManager = require("./dependencyManager"); -var packageJsonPath = path.join(projectDir, "package.json"); -var packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); +const PROJECT_DIR = path.dirname(path.dirname(__dirname)); +const APP_DIR = path.resolve(PROJECT_DIR, "app"); -var isAngular = Object.keys(packageJson.dependencies).filter(function (dependency) { - return /^@angular\b/.test(dependency); -}).length > 0; +function install() { + let packageJson = helpers.getPackageJson(PROJECT_DIR); -var isTypeScript = fs.existsSync(path.join(projectDir, "tsconfig.json")); -if (isAngular) { - isTypeScript = true; -} - -function getProjectTemplates() { - var templates = { - "webpack.android.js.template": "webpack.android.js", - "webpack.ios.js.template": "webpack.ios.js", - }; - - if (isAngular) { - templates["webpack.common.js.angular.template"] = "webpack.common.js"; - templates["tsconfig.aot.json.template"] = "tsconfig.aot.json"; - } else { - templates["webpack.common.js.nativescript.template"] = "webpack.common.js"; - } - return templates; -} - -function getAppTemplates() { - var templates = { - "vendor-platform.android.ts.template": tsOrJs("vendor-platform.android"), - "vendor-platform.ios.ts.template": tsOrJs("vendor-platform.ios"), - }; - - if (isAngular) { - templates["vendor.ts.angular.template"] = tsOrJs("vendor"); - } else { - templates["vendor.ts.nativescript.template"] = tsOrJs("vendor"); - } - return templates; -} - -function addProjectFiles() { - var projectTemplates = getProjectTemplates(); - Object.keys(projectTemplates).forEach(function(templateName) { - var templateDestination = projectTemplates[templateName]; - copyProjectTemplate(templateName, templateDestination); - }); - - var appTemplates = getAppTemplates(); - Object.keys(appTemplates).forEach(function(templateName) { - var templateDestination = appTemplates[templateName]; - copyAppTemplate(templateName, templateDestination); - }); -} -exports.addProjectFiles = addProjectFiles; - -function removeProjectFiles() { - var projectTemplates = getProjectTemplates(); - Object.keys(projectTemplates).forEach(function(templateName) { - var templateDestination = projectTemplates[templateName]; - deleteProjectFile(templateDestination); - }); - - var appTemplates = getAppTemplates(); - Object.keys(appTemplates).forEach(function(templateName) { - var templateDestination = appTemplates[templateName]; - deleteAppFile(templateDestination); - }); -} -exports.removeProjectFiles = removeProjectFiles; - -function getScriptTemplates() { - return { - "ns-bundle": "ns-bundle", - "start-[PLATFORM]-bundle": "npm run ns-bundle --[PLATFORM] --start-app", - "build-[PLATFORM]-bundle": "npm run ns-bundle --[PLATFORM] --build-app", - }; -} - -function getDeprecatedScriptTemplates() { - return [ - "clean-[PLATFORM]", - "prewebpack-[PLATFORM]", - "webpack-[PLATFORM]", - "prestart-[PLATFORM]-bundle", - "start-[PLATFORM]-bundle", - "prebuild-[PLATFORM]-bundle", - "build-[PLATFORM]-bundle", - ] -} - -function addNpmScripts() { - var scriptTemplates = getScriptTemplates(); - Object.keys(scriptTemplates).forEach(function(templateName) { - addPlatformScript(packageJson, templateName, scriptTemplates[templateName]); - }); -} -exports.addNpmScripts = addNpmScripts; + projectFilesManager.addProjectFiles(PROJECT_DIR, APP_DIR); -function removeDeprecatedNpmScripts() { - removeNpmScripts(getDeprecatedScriptTemplates()); -} + let scripts = packageJson.scripts || {}; + scripts = npmScriptsManager.removeDeprecatedNpmScripts(scripts); + scripts = npmScriptsManager.addNpmScripts(scripts); + packageJson.scripts = scripts; -exports.removeDeprecatedNpmScripts = removeDeprecatedNpmScripts; + packageJson.devDependencies = dependencyManager.addProjectDeps(packageJson); -function removeNpmScripts(scriptTemplates) { - var scriptTemplates = scriptTemplates || Object.keys(getScriptTemplates()); - scriptTemplates.forEach(function(templateName) { - removePlatformScripts(packageJson, templateName); - }); + helpers.writePackageJson(packageJson, PROJECT_DIR); } -exports.removeNpmScripts = removeNpmScripts; -function getProjectDependencies() { - var dependencies = { - "webpack": "2.2.0", - "webpack-sources": "~0.1.3", - "copy-webpack-plugin": "~3.0.1", - "raw-loader": "~0.5.1", - "nativescript-css-loader": "~0.26.0", - "resolve-url-loader": "~1.6.0", - "extract-text-webpack-plugin": "~2.0.0-beta.4", - }; +function uninstall() { + let packageJson = helpers.getPackageJson(PROJECT_DIR); - if (isAngular) { - dependencies["@angular/compiler-cli"] = "~4.0.0"; - dependencies["@ngtools/webpack"] = "1.2.13"; - dependencies["typescript"] = "^2.1.0"; - } else { - dependencies["awesome-typescript-loader"] = "~3.0.0-beta.9"; - } - return dependencies; -} + projectFilesManager.removeProjectFiles(PROJECT_DIR, APP_DIR); + npmScriptsManager.removeDeprecatedNpmScripts(packageJson); -function addProjectDependencies() { - configureDevDependencies(packageJson, function (add) { - var dependencies = getProjectDependencies(); - Object.keys(dependencies).forEach(function(dependencyName) { - add(dependencyName, dependencies[dependencyName]); - }); - }); -} -exports.addProjectDependencies = addProjectDependencies; + let scripts = packageJson.scripts; + scripts = npmScriptsManager.removeNpmScripts(scripts); + packageJson.scripts = scripts; -function removeProjectDependencies() { - configureDevDependencies(packageJson, function (add, remove) { - var dependencies = getProjectDependencies(); - Object.keys(dependencies).forEach(function(dependencyName) { - remove(dependencyName); - }); - }); + helpers.writePackageJson(packageJson, PROJECT_DIR); } -exports.removeProjectDependencies = removeProjectDependencies; - - -function addPlatformScript(packageJson, nameTemplate, commandTemplate) { - if (!packageJson.scripts) { - packageJson.scripts = {}; - } - var scripts = packageJson.scripts; - ["android", "ios"].forEach(function (platform) { - var name = nameTemplate.replace(/\[PLATFORM\]/g, platform); - var command = commandTemplate.replace(/\[PLATFORM\]/g, platform); - if (!scripts[name]) { - console.log("Registering script: " + name); - scripts[name] = command; - } - }); -} - -function removePlatformScripts(packageJson, nameTemplate) { - if (!packageJson.scripts) { - return; - } - - var scripts = packageJson.scripts; - ["android", "ios"].forEach(function (platform) { - var name = nameTemplate.replace(/\[PLATFORM\]/g, platform); - console.log("Removing script: " + name); - delete scripts[name]; - }); -} - -function configureDevDependencies(packageJson, adderCallback) { - var pendingNpmInstall = false; - if (!packageJson.devDependencies) { - packageJson.devDependencies = {}; - } - var dependencies = packageJson.devDependencies; - - adderCallback(function (name, version) { - if (!dependencies[name]) { - dependencies[name] = version; - console.info("Adding dev dependency: " + name + "@" + version); - pendingNpmInstall = true; - } else { - console.info("Dev dependency: '" + name + "' already added. Leaving version: " + dependencies[name]); - } - }, function(name) { - console.info("Removing dev dependency: " + name); - delete dependencies[name]; - }); - - fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); - - if (pendingNpmInstall) { - console.info("Installing new dependencies..."); - //Run `npm install` after everything else. - setTimeout(function () { - var spawnArgs = []; - if (/^win/.test(process.platform)) { - spawnArgs = ["cmd.exe", ["/c", "npm", "install"]]; - } else { - spawnArgs = ["npm", ["install"]]; - } - spawnArgs.push({ cwd: projectDir, stdio: "inherit" }); - var npm = childProcess.spawn.apply(null, spawnArgs); - npm.on("close", function (code) { - process.exit(code); - }); - }, 100); - } -} - -function tsOrJs(name) { - if (isTypeScript) { - return name + ".ts"; - } else { - return name + ".js"; - } -} - -function copyProjectTemplate(templateName, projectPath) { - var destinationPath = path.join(projectDir, projectPath); - copyTemplate(templateName, destinationPath); -} - -function deleteProjectFile(projectPath) { - var destinationPath = path.join(projectDir, projectPath); - deleteFile(destinationPath); -} - -function copyAppTemplate(templateName, appPath) { - var destinationPath = path.join(appDir, appPath); - copyTemplate(templateName, destinationPath); -} - -function deleteAppFile(appPath) { - var destinationPath = path.join(appDir, appPath); - deleteFile(destinationPath); -} - -function deleteFile(destinationPath) { - if (fs.existsSync(destinationPath)) { - console.log("Deleting file: " + destinationPath); - fs.unlink(destinationPath); - } -} - -function copyTemplate(templateName, destinationPath) { - var templatePath = path.join(__dirname, templateName); - // Create destination file, only if not present. - if (!fs.existsSync(destinationPath)) { - console.log("Creating file: " + destinationPath); - var content = fs.readFileSync(templatePath, "utf8"); - fs.writeFileSync(destinationPath, content); - } -} +module.exports = { + install, + uninstall, +}; diff --git a/npmScriptsManager.js b/npmScriptsManager.js new file mode 100644 index 00000000..7ed09a15 --- /dev/null +++ b/npmScriptsManager.js @@ -0,0 +1,74 @@ +const SCRIPT_TEMPLATES = Object.freeze({ + "ns-bundle": "ns-bundle", + "start-[PLATFORM]-bundle": "npm run ns-bundle --[PLATFORM] --start-app", + "build-[PLATFORM]-bundle": "npm run ns-bundle --[PLATFORM] --build-app", +}); + +const DEPRECATED_SCRIPT_TEMPLATES = Object.freeze([ + "clean-[PLATFORM]", + "prewebpack-[PLATFORM]", + "webpack-[PLATFORM]", + "prestart-[PLATFORM]-bundle", + "start-[PLATFORM]-bundle", + "prebuild-[PLATFORM]-bundle", + "build-[PLATFORM]-bundle", +]); + +const PLATFORMS = Object.freeze(["android", "ios"]); + +function addNpmScripts(scripts) { + Object.keys(SCRIPT_TEMPLATES).forEach(name => { + packageJson = addPlatformScript(scripts, name, SCRIPT_TEMPLATES[name]); + }); + + return scripts; +} + +function removeDeprecatedNpmScripts(scripts) { + return removeNpmScripts(scripts, DEPRECATED_SCRIPT_TEMPLATES); +} + +function removeNpmScripts(scripts, scriptTemplates = Object.keys(SCRIPT_TEMPLATES)) { + scriptTemplates.forEach(function(templateName) { + scripts = removePlatformScripts(scripts, templateName); + }); + + return scripts; +} + +function addPlatformScript(scripts, nameTemplate, commandTemplate) { + PLATFORMS.forEach(function (platform) { + const name = nameTemplate.replace(/\[PLATFORM\]/g, platform); + const command = commandTemplate.replace(/\[PLATFORM\]/g, platform); + + if (!scripts[name]) { + console.info(`Registering script: ${name}`); + scripts[name] = command; + } + }); + + return scripts; +} + +function removePlatformScripts(scripts, nameTemplate) { + if (!scripts) { + return; + } + + PLATFORMS.forEach(function (platform) { + const name = nameTemplate.replace(/\[PLATFORM\]/g, platform); + + if (scripts[name]) { + console.info(`Removing script: ${name}`); + delete scripts[name]; + } + }); + + return scripts; +} + +module.exports = { + addNpmScripts, + removeDeprecatedNpmScripts, + removeNpmScripts, +}; diff --git a/package.json b/package.json index 42843d5b..df28686e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nativescript-dev-webpack", - "version": "0.3.7", + "version": "0.4.0", "main": "index", "description": "", "homepage": "http://www.telerik.com", @@ -19,6 +19,7 @@ "bin": { "install-ns-webpack": "./bin/install-ns-webpack", "remove-ns-webpack": "./bin/remove-ns-webpack", + "update-ns-webpack": "./bin/update-ns-webpack", "ns-bundle": "./bin/ns-bundle", "ns-verify-bundle": "./bin/ns-verify-bundle" }, diff --git a/postinstall.js b/postinstall.js index e252b2b6..9b839ad2 100644 --- a/postinstall.js +++ b/postinstall.js @@ -1,6 +1,2 @@ -var installer = require("./installer"); - -installer.addProjectFiles(); -installer.removeDeprecatedNpmScripts(); -installer.addNpmScripts(); -installer.addProjectDependencies(); +const installer = require("./installer"); +installer.install(); diff --git a/projectFilesManager.js b/projectFilesManager.js new file mode 100644 index 00000000..e4e58ba3 --- /dev/null +++ b/projectFilesManager.js @@ -0,0 +1,109 @@ +const path = require("path"); +const fs = require("fs"); + +const helpers = require("./projectHelpers"); + +function addProjectFiles(projectDir, appDir) { + 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) { + 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 deleteFile(destinationPath) { + if (fs.existsSync(destinationPath)) { + console.info(`Deleting file: ${destinationPath}`); + fs.unlink(destinationPath); + } +} + +function copyTemplate(templateName, destinationPath) { + // Create destination file, only if not present. + if (!fs.existsSync(destinationPath)) { + console.info(`Creating file: ${destinationPath}`); + const content = fs.readFileSync(templateName, "utf8"); + fs.writeFileSync(destinationPath, content); + } +} + +function getProjectTemplates(projectDir) { + let templates = { + "webpack.android.js.template": "webpack.android.js", + "webpack.ios.js.template": "webpack.ios.js", + }; + + if (helpers.isAngular({projectDir})) { + templates["webpack.common.js.angular.template"] = "webpack.common.js"; + templates["tsconfig.aot.json.template"] = "tsconfig.aot.json"; + } else if (helpers.isTypeScript({projectDir})) { + templates["webpack.common.js.typescript.template"] = "webpack.common.js"; + } else { + templates["webpack.common.js.javascript.template"] = "webpack.common.js"; + } + + return getFullTemplatesPath(projectDir, templates); +} + +function getAppTemplates(projectDir, appDir) { + const templates = { + "vendor-platform.android.ts.template": tsOrJs(projectDir, "vendor-platform.android"), + "vendor-platform.ios.ts.template": tsOrJs(projectDir, "vendor-platform.ios"), + }; + + if (helpers.isAngular({projectDir})) { + templates["vendor.ts.angular.template"] = tsOrJs(projectDir, "vendor"); + } else { + templates["vendor.ts.nativescript.template"] = tsOrJs(projectDir, "vendor"); + } + + return getFullTemplatesPath(appDir, templates); +} + +function getFullTemplatesPath(projectDir, templates) { + let updatedTemplates = {}; + + Object.keys(templates).forEach(key => { + const updatedKey = getFullPath(__dirname, key); + const updatedValue = getFullPath(projectDir, templates[key]) + + updatedTemplates[updatedKey] = updatedValue; + }); + + return updatedTemplates; +} + +function getFullPath(projectDir, filePath) { + return path.resolve(projectDir, filePath); +} + +function tsOrJs(projectDir, name) { + const extension = helpers.isTypeScript({projectDir}) ? "ts" : "js"; + return `${name}.${extension}`; +} + +module.exports = { + addProjectFiles, + removeProjectFiles, +}; diff --git a/projectHelpers.js b/projectHelpers.js new file mode 100644 index 00000000..903cb384 --- /dev/null +++ b/projectHelpers.js @@ -0,0 +1,36 @@ +const path = require("path"); +const fs = require("fs"); + +const isTypeScript = ({projectDir, packageJson} = {}) => { + packageJson = packageJson || getPackageJson(projectDir); + + return packageJson.dependencies.hasOwnProperty("typescript") || + packageJson.devDependencies.hasOwnProperty("typescript") || + isAngular({packageJson}); +}; + +const isAngular = ({projectDir, packageJson} = {}) => { + packageJson = packageJson || getPackageJson(projectDir); + + return Object.keys(packageJson.dependencies) + .some(dependency => /^@angular\b/.test(dependency)); +}; + +const getPackageJson = projectDir => { + const packageJsonPath = getPackageJsonPath(projectDir); + return JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); +}; + +const writePackageJson = (content, projectDir) => { + const packageJsonPath = getPackageJsonPath(projectDir); + fs.writeFileSync(packageJsonPath, JSON.stringify(content, null, 2)) +} + +const getPackageJsonPath = projectDir => path.resolve(projectDir, "package.json"); + +module.exports = { + isTypeScript, + isAngular, + getPackageJson, + writePackageJson, +}; diff --git a/should-not-be-here b/should-not-be-here new file mode 100644 index 00000000..e69de29b diff --git a/tsconfig.aot.json.template b/tsconfig.aot.json.template index b1221e9a..23e3021c 100644 --- a/tsconfig.aot.json.template +++ b/tsconfig.aot.json.template @@ -1,5 +1,5 @@ { - "extend": "./tsconfig", + "extends": "./tsconfig", "compilerOptions": { "baseUrl": ".", "paths": { @@ -28,6 +28,10 @@ "globals": ["node_modules/tns-core-modules/globals"] } }, + "exclude": [ + "node_modules", + "platforms" + ], "angularCompilerOptions": { "skipMetadataEmit": true, "genDir": "./" diff --git a/webpack.common.js.angular.template b/webpack.common.js.angular.template index b05c860e..0d017ebd 100644 --- a/webpack.common.js.angular.template +++ b/webpack.common.js.angular.template @@ -12,24 +12,24 @@ module.exports = function (platform, destinationApp) { destinationApp = nsWebpack.getAppPath(platform); } var entry = {}; - //Discover entry module from package.json + // Discover entry module from package.json entry.bundle = "./" + nsWebpack.getEntryModule(); //Vendor entry with third party libraries. entry.vendor = "./vendor"; - //app.css bundle + // app.css bundle entry["app.css"] = "./app.css"; var plugins = [ new ExtractTextPlugin("app.css"), - //Vendor libs go to the vendor.js chunk + // Vendor libs go to the vendor.js chunk new webpack.optimize.CommonsChunkPlugin({ name: ["vendor"] }), - //Define useful constants like TNS_WEBPACK + // Define useful constants like TNS_WEBPACK new webpack.DefinePlugin({ "global.TNS_WEBPACK": "true", }), - //Copy assets to out dir. Add your own globs as needed. + // Copy assets to out dir. Add your own globs as needed. new CopyWebpackPlugin([ { from: "app.css" }, { from: "css/**" }, @@ -38,13 +38,13 @@ module.exports = function (platform, destinationApp) { { from: "**/*.png" }, { from: "**/*.xml" }, ], { ignore: ["App_Resources/**"] }), - //Generate a bundle starter script and activate it in package.json + // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ "./vendor", "./bundle", ]), - //Angular AOT compiler + // Angular AOT compiler new AotPlugin({ tsConfigPath: "tsconfig.aot.json", entryModule: path.resolve(__dirname, "app/app.module#AppModule"), @@ -58,7 +58,7 @@ module.exports = function (platform, destinationApp) { minimize: true })); - //Work around an Android issue by setting compress = false + // Work around an Android issue by setting compress = false var compress = platform !== "android"; plugins.push(new webpack.optimize.UglifyJsPlugin({ mangle: { @@ -79,7 +79,7 @@ module.exports = function (platform, destinationApp) { filename: "[name].js", }, resolve: { - //Resolve platform-specific modules like module.android.js + // Resolve platform-specific modules like module.android.js extensions: [ ".aot.ts", ".ts", @@ -89,14 +89,14 @@ module.exports = function (platform, destinationApp) { "." + platform + ".js", "." + platform + ".css", ], - //Resolve {N} system modules from tns-core-modules + // Resolve {N} system modules from tns-core-modules modules: [ "node_modules/tns-core-modules", - "node_modules" + "node_modules", ] }, node: { - //Disable node shims that conflict with NativeScript + // Disable node shims that conflict with NativeScript "http": false, "timers": false, "setImmediate": false, @@ -141,7 +141,7 @@ module.exports = function (platform, destinationApp) { loaders: [ "raw-loader", "resolve-url-loader", - "sass-loader" + "sass-loader", ] }, ] diff --git a/webpack.common.js.javascript.template b/webpack.common.js.javascript.template new file mode 100644 index 00000000..7f44fda8 --- /dev/null +++ b/webpack.common.js.javascript.template @@ -0,0 +1,129 @@ +var webpack = require("webpack"); +var nsWebpack = require("nativescript-dev-webpack"); +var nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); +var path = require("path"); +var CopyWebpackPlugin = require("copy-webpack-plugin"); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); + +module.exports = function (platform, destinationApp) { + if (!destinationApp) { + // Default destination inside platforms//... + destinationApp = nsWebpack.getAppPath(platform); + } + var entry = {}; + // Discover entry module from package.json + entry.bundle = "./" + nsWebpack.getEntryModule(); + // Vendor entry with third party libraries. + entry.vendor = "./vendor"; + // app.css bundle + entry["app.css"] = "./app.css"; + + var plugins = [ + new ExtractTextPlugin("app.css"), + // 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", + }), + // Copy assets to out dir. Add your own globs as needed. + new CopyWebpackPlugin([ + { from: "app.css" }, + { from: "css/**" }, + { from: "fonts/**" }, + { from: "**/*.jpg" }, + { from: "**/*.png" }, + { from: "**/*.xml" }, + ], { ignore: ["App_Resources/**"] }), + // Generate a bundle starter script and activate it in package.json + new nsWebpack.GenerateBundleStarterPlugin([ + "./vendor", + "./bundle", + ]), + ]; + + if (process.env.npm_config_uglify) { + plugins.push(new webpack.LoaderOptionsPlugin({ + minimize: true + })); + + // Work around an Android issue by setting compress = false + var compress = platform !== "android"; + plugins.push(new webpack.optimize.UglifyJsPlugin({ + mangle: { + except: nsWebpack.uglifyMangleExcludes, + }, + compress: compress, + })); + } + + return { + context: path.resolve("./app"), + target: nativescriptTarget, + entry: entry, + output: { + pathinfo: true, + path: path.resolve(destinationApp), + libraryTarget: "commonjs2", + filename: "[name].js", + }, + resolve: { + // Resolve platform-specific modules like module.android.js + extensions: [ + ".js", + ".css", + "." + platform + ".js", + "." + platform + ".css", + ], + // Resolve {N} system modules from tns-core-modules + modules: [ + "node_modules/tns-core-modules", + "node_modules", + ] + }, + node: { + // Disable node shims that conflict with NativeScript + "http": false, + "timers": false, + "setImmediate": false, + "fs": "empty", + }, + module: { + loaders: [ + { + test: /\.html$/, + loader: "raw-loader" + }, + // Root app.css file gets extracted with bundled dependencies + { + test: /app\.css$/, + loader: ExtractTextPlugin.extract([ + "resolve-url-loader", + "nativescript-css-loader", + "nativescript-dev-webpack/platform-css-loader", + ]), + }, + // Other CSS files get bundled using the raw loader + { + test: /\.css$/, + exclude: /app\.css$/, + loaders: [ + "raw-loader", + ] + }, + // SASS support + { + test: /\.scss$/, + loaders: [ + "raw-loader", + "resolve-url-loader", + "sass-loader", + ] + }, + ] + }, + plugins: plugins, + }; +}; diff --git a/webpack.common.js.nativescript.template b/webpack.common.js.typescript.template similarity index 82% rename from webpack.common.js.nativescript.template rename to webpack.common.js.typescript.template index 531f7327..8427c542 100644 --- a/webpack.common.js.nativescript.template +++ b/webpack.common.js.typescript.template @@ -7,28 +7,28 @@ var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = function (platform, destinationApp) { if (!destinationApp) { - //Default destination inside platforms//... + // Default destination inside platforms//... destinationApp = nsWebpack.getAppPath(platform); } var entry = {}; - //Discover entry module from package.json + // Discover entry module from package.json entry.bundle = "./" + nsWebpack.getEntryModule(); - //Vendor entry with third party libraries. + // Vendor entry with third party libraries. entry.vendor = "./vendor"; - //app.css bundle + // app.css bundle entry["app.css"] = "./app.css"; var plugins = [ new ExtractTextPlugin("app.css"), - //Vendor libs go to the vendor.js chunk + // Vendor libs go to the vendor.js chunk new webpack.optimize.CommonsChunkPlugin({ name: ["vendor"] }), - //Define useful constants like TNS_WEBPACK + // Define useful constants like TNS_WEBPACK new webpack.DefinePlugin({ "global.TNS_WEBPACK": "true", }), - //Copy assets to out dir. Add your own globs as needed. + // Copy assets to out dir. Add your own globs as needed. new CopyWebpackPlugin([ { from: "app.css" }, { from: "css/**" }, @@ -37,7 +37,7 @@ module.exports = function (platform, destinationApp) { { from: "**/*.png" }, { from: "**/*.xml" }, ], { ignore: ["App_Resources/**"] }), - //Generate a bundle starter script and activate it in package.json + // Generate a bundle starter script and activate it in package.json new nsWebpack.GenerateBundleStarterPlugin([ "./vendor", "./bundle", @@ -49,7 +49,7 @@ module.exports = function (platform, destinationApp) { minimize: true })); - //Work around an Android issue by setting compress = false + // Work around an Android issue by setting compress = false var compress = platform !== "android"; plugins.push(new webpack.optimize.UglifyJsPlugin({ mangle: { @@ -70,7 +70,7 @@ module.exports = function (platform, destinationApp) { filename: "[name].js", }, resolve: { - //Resolve platform-specific modules like module.android.js + // Resolve platform-specific modules like module.android.js extensions: [ ".ts", ".js", @@ -79,14 +79,14 @@ module.exports = function (platform, destinationApp) { "." + platform + ".js", "." + platform + ".css", ], - //Resolve {N} system modules from tns-core-modules + // Resolve {N} system modules from tns-core-modules modules: [ "node_modules/tns-core-modules", - "node_modules" + "node_modules", ] }, node: { - //Disable node shims that conflict with NativeScript + // Disable node shims that conflict with NativeScript "http": false, "timers": false, "setImmediate": false, @@ -119,7 +119,7 @@ module.exports = function (platform, destinationApp) { { test: /\.ts$/, loaders: [ - "awesome-typescript-loader" + "awesome-typescript-loader", ] }, // SASS support @@ -128,7 +128,7 @@ module.exports = function (platform, destinationApp) { loaders: [ "raw-loader", "resolve-url-loader", - "sass-loader" + "sass-loader", ] }, ]