UNPKG

angular-ide

Version:

Provides a seamless integration with the Angular IDE from the command-line for developers looking for an enhanced development experience with Angular.

465 lines (398 loc) 16.2 kB
const path = require('path'); const jju = require('jju'); const chalk = require('chalk'); const Q = require('q'); const winston = require('winston'); const fs = require('fs-extra'); const moment = require('moment'); const slashes = require('slashes'); function updatePackage(updateOptions) { const date = moment().format('YYYYMMDD'); const packageBackupFilename = `package.json.ng-update.${date}.bak`; try { fs.copySync(path.resolve('package.json'), packageBackupFilename); } catch (e) { winston.error('[package.json] Error while backing up package.json'); throw e; } const packageContent = fs.readFileSync(path.resolve('package.json')).toString('utf8'); const parsedJSON = jju.parse(packageContent, { mode: 'json' }); //update dependencies depsUpdates = { '@angular/*': '^4.0.0', 'rxjs': '^5.1.0', 'zone.js': '^0.8.4', 'ts-helpers': null, } const depsV4Filter = [ '@angular/animations', '@angular/common', '@angular/compiler', '@angular/compiler-cli', '@angular/core', '@angular/forms', '@angular/http', '@angular/language-service', '@angular/platform-browser', '@angular/platform-browser-dynamic', '@angular/platform-server', '@angular/platform-webworker', '@angular/platform-webworker-dynamic', '@angular/router', '@angular/tsc-wrapped', '@angular/upgrade', ]; devDepsUpdate = { '@angular/*' : "^4.0.0", '@angular/compiler-cli' : "^4.0.0", '@angular/cli' : "1.0.0", 'angular-cli': null, "@types/jasmine": "2.5.38", "@types/node": "~6.0.60", "codelyzer": "~2.0.0", "jasmine-core": "~2.5.2", "jasmine-spec-reporter": "~3.2.0", "karma": "~1.4.1", "karma-chrome-launcher": "~2.0.0", "karma-cli": "~1.0.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", "karma-coverage-istanbul-reporter": "^0.2.0", "protractor": "~5.1.0", "ts-node": "~2.0.0", "tslint": "~4.5.0", "typescript": "~2.2.0", 'karma-remap-istanbul': null, } touchedDeps = new Set(); updateDeps(parsedJSON, devDepsUpdate, "peerDependencies", false); updateDeps(parsedJSON, depsUpdates, "peerDependencies", false); updateDeps(parsedJSON, depsUpdates, "dependencies", true); updateDeps(parsedJSON, devDepsUpdate, "devDependencies", true); function updateDeps(parsedJSON, toUpdate, name, add) { if (typeof parsedJSON[name] !== 'undefined') { var curDeps = parsedJSON[name]; Object.keys(toUpdate).forEach(function(dep) { if (toUpdate[dep]) { var newVersion = toUpdate[dep]; if (dep.endsWith("*")) { var toMatch = dep.substring(0, dep.length - 1); Object.keys(curDeps).forEach((curDep) => { if (curDep.match('@angular/') && depsV4Filter.indexOf(curDep) !== -1 && !toUpdate.hasOwnProperty(curDep)) { const currentVersion = curDeps[curDep]; if (currentVersion !== newVersion) { winston.info(`[package.json] [${name}] Updating ${curDep}@${currentVersion} => ${newVersion}`); curDeps[curDep] = newVersion; touchedDeps.add(curDep); } } }); } else { const currentVersion = curDeps[dep]; if (currentVersion !== newVersion) { if (currentVersion == undefined) { if (add && !touchedDeps.has(dep)) { winston.info(`[package.json] [${name}] Adding ${dep}@${newVersion}`); curDeps[dep] = newVersion; touchedDeps.add(dep); } } else { winston.info(`[package.json] [${name}] Updating ${dep}@${currentVersion} => ${newVersion}`); curDeps[dep] = newVersion; touchedDeps.add(dep); } } } } else if (curDeps[dep]) { delete curDeps[dep]; touchedDeps.add(dep); winston.info(`[package.json] [${name}] Removing ${dep}@${curDeps[dep]}`); } }); } else { winston.info(`[package.json] [${name}] Doesn't exists, doing nothing`); } } //update scripts if (parsedJSON.scripts) { const scripts = parsedJSON.scripts; if (scripts["lint"] === "tslint \"src/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check") { winston.info(`[package.json] [scripts] Updating lint to "ng lint"`); scripts["lint"] = "ng lint"; } if (scripts["e2e"] === "protractor") { winston.info(`[package.json] [scripts] Updating e2e to "ng e2e"`) scripts["e2e"] = "ng e2e"; winston.info(`[package.json] [scripts] Deleting pree2e`) delete scripts["pree2e"]; } if (!scripts["build"]) { winston.info(`[package.json] [scripts] Adding build with "ng build"`); scripts["build"] = "ng build"; } } const finalPackageContent = jju.update(packageContent, parsedJSON, { mode: 'json' }); if (!updateOptions.dryRun) { fs.writeFileSync(path.resolve('package.json'), finalPackageContent); } else { winston.info('[package.json]', finalPackageContent); } } function updateTSLint(updateOptions, angularCliJSONPath) { const date = moment().format('YYYYMMDD'); const tsLintBackupFilename = `tslint.json.ng-update.${date}.bak`; try { fs.copySync(path.resolve('tslint.json'), tsLintBackupFilename); } catch (e) { winston.error('[tslint.json] Error while backing up tslint.json'); throw e; } const tsLintConfigPath = path.resolve('tslint.json'); const tslintContent = fs.readFileSync(tsLintConfigPath).toString('utf8'); const tslintParsed = jju.parse(tslintContent, { mode : 'json' }); const angularCliContent = fs.readFileSync(angularCliJSONPath); const angularCliJSON = jju.parse(angularCliContent); const componentPrefix = angularCliJSON.apps[0].prefix; const rulesUpdate = { 'no-duplicate-key' : null, 'no-unreachable' : null, 'label-undefined' : null, 'directive-selector-prefix' : null, 'directive-selector-name' : null, 'directive-selector-type' : null, 'component-selector-type' : null, 'component-selector-prefix' : null, 'component-selector-name' : null, 'callable-types' : true, 'import-blacklist' : [ true, 'rxjs' ], 'import-spacing' : true, 'interface-over-type-literal' : true, 'no-empty-interface' : true, 'no-string-throw' : true, 'prefer-const' : true, 'typeof-compare' : true, 'unified-signatures' : true, 'directive-selector' : [ true, 'attribute', componentPrefix, 'camelCase' ], 'component-selector' : [ true, 'element', componentPrefix, 'kebab-case' ], "no-access-missing-member": true, "templates-use-public": true, "invoke-injectable": true }; Object.keys(rulesUpdate).forEach((key) => { if (rulesUpdate[key]) { if (typeof tslintParsed.rules[key] === 'undefined') { winston.info(`[TSLint] [rules] Adding "${key}"`); tslintParsed.rules[key] = rulesUpdate[key]; } } else if (tslintParsed.rules[key]) { winston.info(`[TSLint] [rules] Removing "${key}"`); delete tslintParsed.rules[key]; } }); const finalTSLintContent = jju.update(tslintContent, tslintParsed, { mode : 'json' }); if (!updateOptions.dryRun) { return Q.nfcall(fs.writeFile, tsLintConfigPath, finalTSLintContent); } else { winston.info(finalTSLintContent); } } function writeUpdateReference(referenceFilePath) { const referenceFileContent = ` # Upgrading to Angular 4 Congratulations! You are well on your way to having this project updated to use Angular 4. Angular IDE helps get you a lot of the way there, though check out the steps below for other notes that may be important for you to follow. ## Tasks Completed Tasks below have been performed during the upgrade. In some cases, tasks may have been skipped if already ready for Angular 4. 1. Update package.json to use Angular 4 modules 2. Download dependences of packages via npm 3. Use @angular/cli instead of the earlier angular-cli 4. Rename angular-cli.json to .angular-cli.json 5. Add $schema property to .angular-cli.json 6. Update polyfills, envronments & linting configuraton 7. Update tslint.json with new rules 8. Update lifecycle methods such as OnInit to be implements 9. Change \`\`<template>\`\` to \`\`<ng-template>\`\` in template files ## Tasks Remaining Tasks below can be completed though not all are required. More details can be found online at: https://github.com/angular/angular-cli/wiki/stories-1.0-update ### a. Update Generator Defaults in .angular-cli.json You can now list the flags as they appear on the generator command: "defaults": { "styleExt": "css", "component": { "inlineTemplate": false, "spec": true } } See more here https://github.com/angular/angular-cli/wiki/generate-component ### b. Switch to one tsconfig.json per application Optionally, split the tsconfigs into multiple files: * src/tsconfig.app.json: configuration for the Angular app. * src/tsconfig.spec.json: configuration for the unit tests. Defaults to the Angular app config. * e2e/tsconfig.e2e.json: configuration for the e2e tests. There is an additional root-level tsconfig.json that is used for editor integration. See more here https://github.com/angular/angular-cli/wiki/stories-1.0-update#one-tsconfig-per-app ### c. Update karma.conf.js This files needs to get updated to use the new Angular CLI package name (@angular/cli) plus some minor changes on the reporters config. See more here https://github.com/angular/angular-cli/wiki/stories-1.0-update#karmaconfjs ### d. Update protractor.conf.js To make Protractor to play well with the changes, protractor.conf.js needs to get updated to use the new tsconfigs described in _Switch to one tsconfig.json per application_. See more here https://github.com/angular/angular-cli/wiki/stories-1.0-update#protractorconfjs ... `; winston.info('[update-reference] Writing update reference file'); return Q.nfcall(fs.writeFile, referenceFilePath, referenceFileContent); } function updateAngularCLIConfig(updateOptions, angularCliJSONPath) { const date = moment().format('YYYYMMDD'); const angularCliBackupFilename = `.angular-cli.json.ng-update.${date}.bak`; try { fs.copySync(path.resolve('.angular-cli.json'), angularCliBackupFilename); } catch (e) { winston.error('[.angular-cli.json] Error while backing up .angular-cli.json'); throw e; } const angularCliContent = fs.readFileSync(angularCliJSONPath).toString(); const angularCliJSON = jju.parse(angularCliContent); if (!angularCliJSON['$schema']) { winston.debug(`[.angular-cli.json] Adding $schema`); angularCliJSON['$schema'] = './node_modules/@angular/cli/lib/config/schema.json'; } if (angularCliJSON.apps[0]) { const app = angularCliJSON.apps[0]; if (app.environmentSource === undefined) { winston.debug(`[.angular-cli.json] [apps[0]] Adding "environmentSource"`); app.environmentSource = 'environments/environment.ts'; winston.debug(`[.angular-cli.json] [apps[0]] Removing deprecated "environments.source"`); delete app.environments.source; } if (app.polyfills === undefined) { winston.debug(`[.angular-cli.json] [apps[0]] Adding "polyfills"`); app.polyfills = 'polyfills.ts'; } if (app.mobile !== undefined) { winston.debug(`[.angular-cli.json] [apps[0]] Removing deprecated "mobile" entry`); delete app.mobile; } } if (angularCliJSON.addons !== undefined) { winston.debug(`[.angular-cli.json] Removing deprecated "addons" object.`); delete angularCliJSON.addons; } if (angularCliJSON.packages !== undefined) { winston.debug(`[.angular-cli.json] Removing deprecated "packages" object.`); delete angularCliJSON.packages; } if (angularCliJSON.project && angularCliJSON.project.version) { winston.debug(`[.angular-cli.json] [project] Removing deprecated "version" specification.`); delete angularCliJSON.project.version; } if (angularCliJSON.lint === undefined) { var lint = angularCliJSON.lint = []; if (!(tryAdding('src/tsconfig.app.json') || tryAdding('src/tsconfig.spec.json'))) { // Try to support legacy location of a config file tryAdding('src/tsconfig.json'); } if (!tryAdding('e2e/tsconfig.e2e.json')) { // Try to support legacy location of a config file tryAdding('e2e/tsconfig.json'); } function tryAdding(name) { try { fs.statSync(path.resolve(name)); lint.push({ project: name}); winston.debug(`[.angular-cli.json] [lint] Adding linting configuration for ${name}`); return true; } catch (e) { return false; } } } const finalAngularCLIConfig = jju.update(angularCliContent, angularCliJSON, { mode: 'json' }); if (!updateOptions.dryRun) { return Q.nfcall(fs.writeFile, angularCliJSONPath, finalAngularCLIConfig); } else { winston.info(finalAngularCLIConfig); } } module.exports = function update(updateOptions) { let logFilePath = updateOptions.logFile ? updateOptions.logFile : 'angular-cli-update.log'; if (process.platform === 'win32') { logFilePath = slashes.add(logFilePath); } logFilePath = path.resolve(logFilePath); const logStream = fs.createWriteStream(logFilePath, { flags: 'w' }); winston.add(winston.transports.File, { stream: logStream, json: false }); let updateReferenceFilePath = updateOptions.updateReferenceFile ? updateOptions.updateReferenceFile : 'angular-cli-update.md'; if (process.platform === 'win32') { updateReferenceFilePath = slashes.add(updateReferenceFilePath); } updateReferenceFilePath = path.resolve(updateReferenceFilePath); const filesNeededForUpdate = [ 'tslint.json', 'package.json' ]; const filesNeededPromises = filesNeededForUpdate.map((file) => { const filePath = path.resolve(file); return Q.Promise((resolve, reject) => { fs.access(filePath, fs.constants.R_OK | fs.constants.W_OK, (fileExistanceError) => { if (fileExistanceError) { reject(`[update] File doesn't exists ${file}`); } else { resolve(file); } }); }); }); const angularCliJSONPath = path.resolve('.angular-cli.json'); const oldAngularCliJSONPath = path.resolve('angular-cli.json'); Q.all(filesNeededPromises) .then(() => { // Renaming angular-cli.json if needed return Q.Promise((resolve, reject) => { fs.access(angularCliJSONPath, fs.constants.R_OK | fs.constants.W_OK, (angularCliExistanceError) => { if (angularCliExistanceError) { fs.access(oldAngularCliJSONPath, fs.constants.R_OK | fs.constants.W_OK, (oldAngularCliExistanceError) => { if (!oldAngularCliExistanceError) { fs.renameSync(oldAngularCliJSONPath, angularCliJSONPath); winston.info('[.angular-cli.json] Renaming angular-cli.json to .angular-cli.json'); resolve(); } else { reject('[.angular-cli.json] No angular-cli.json found!'); } }); } else { resolve(); } }); }); }) .then(() => { try { updatePackage(updateOptions); } catch (e) { winston.error(chalk.red('Error when trying to update package.json')); throw e; } }) .then(() => { return updateAngularCLIConfig(updateOptions, angularCliJSONPath); }) .then(() => { return updateTSLint(updateOptions, angularCliJSONPath); }) .then(() => { return writeUpdateReference(updateReferenceFilePath); }) .catch((e) => { winston.error(chalk.red(e)); logStream.end(); process.exit(-1) }) .fin(() => { logStream.end(); process.exit(); }); };