index.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. /**
  4. * @license
  5. * Copyright Google Inc. All Rights Reserved.
  6. *
  7. * Use of this source code is governed by an MIT-style license that can be
  8. * found in the LICENSE file at https://angular.io/license
  9. */
  10. const core_1 = require("@angular-devkit/core");
  11. const schematics_1 = require("@angular-devkit/schematics");
  12. const tasks_1 = require("@angular-devkit/schematics/tasks");
  13. const dependencies_1 = require("../../utility/dependencies");
  14. const json_utils_1 = require("../../utility/json-utils");
  15. const latest_versions_1 = require("../../utility/latest-versions");
  16. const defaults = {
  17. appRoot: 'src',
  18. index: 'index.html',
  19. main: 'main.ts',
  20. polyfills: 'polyfills.ts',
  21. tsConfig: 'tsconfig.app.json',
  22. test: 'test.ts',
  23. outDir: 'dist/',
  24. karma: 'karma.conf.js',
  25. protractor: 'protractor.conf.js',
  26. testTsConfig: 'tsconfig.spec.json',
  27. serverOutDir: 'dist-server',
  28. serverMain: 'main.server.ts',
  29. serverTsConfig: 'tsconfig.server.json',
  30. };
  31. function getConfigPath(tree) {
  32. let possiblePath = core_1.normalize('.angular-cli.json');
  33. if (tree.exists(possiblePath)) {
  34. return possiblePath;
  35. }
  36. possiblePath = core_1.normalize('angular-cli.json');
  37. if (tree.exists(possiblePath)) {
  38. return possiblePath;
  39. }
  40. throw new schematics_1.SchematicsException('Could not find configuration file');
  41. }
  42. function migrateKarmaConfiguration(config) {
  43. return (host, context) => {
  44. context.logger.info(`Updating karma configuration`);
  45. try {
  46. const karmaPath = config && config.test && config.test.karma && config.test.karma.config
  47. ? config.test.karma.config
  48. : defaults.karma;
  49. const buffer = host.read(karmaPath);
  50. if (buffer !== null) {
  51. let content = buffer.toString();
  52. // Replace the 1.0 files and preprocessor entries, with and without comma at the end.
  53. // If these remain, they will cause the `ng test` to fail.
  54. content = content.replace(`{ pattern: './src/test.ts', watched: false },`, '');
  55. content = content.replace(`{ pattern: './src/test.ts', watched: false }`, '');
  56. content = content.replace(`'./src/test.ts': ['@angular/cli'],`, '');
  57. content = content.replace(`'./src/test.ts': ['@angular/cli']`, '');
  58. content = content.replace(/angularCli[^}]*},?/, '');
  59. // Replace 1.x plugin names.
  60. content = content.replace(/@angular\/cli/g, '@angular-devkit/build-angular');
  61. // Replace code coverage output path.
  62. content = content.replace('reports', `dir: require('path').join(__dirname, 'coverage'), reports`);
  63. host.overwrite(karmaPath, content);
  64. }
  65. }
  66. catch (_a) { }
  67. return host;
  68. };
  69. }
  70. function migrateConfiguration(oldConfig, logger) {
  71. return (host, context) => {
  72. const oldConfigPath = getConfigPath(host);
  73. const configPath = core_1.normalize('angular.json');
  74. context.logger.info(`Updating configuration`);
  75. const config = {
  76. '$schema': './node_modules/@angular/cli/lib/config/schema.json',
  77. version: 1,
  78. newProjectRoot: 'projects',
  79. projects: extractProjectsConfig(oldConfig, host, logger),
  80. };
  81. const defaultProject = extractDefaultProject(oldConfig);
  82. if (defaultProject !== null) {
  83. config.defaultProject = defaultProject;
  84. }
  85. const cliConfig = extractCliConfig(oldConfig);
  86. if (cliConfig !== null) {
  87. config.cli = cliConfig;
  88. }
  89. const schematicsConfig = extractSchematicsConfig(oldConfig);
  90. if (schematicsConfig !== null) {
  91. config.schematics = schematicsConfig;
  92. }
  93. const targetsConfig = extractTargetsConfig(oldConfig);
  94. if (targetsConfig !== null) {
  95. config.architect = targetsConfig;
  96. }
  97. context.logger.info(`Removing old config file (${oldConfigPath})`);
  98. host.delete(oldConfigPath);
  99. context.logger.info(`Writing config file (${configPath})`);
  100. host.create(configPath, JSON.stringify(config, null, 2));
  101. return host;
  102. };
  103. }
  104. function extractCliConfig(config) {
  105. const newConfig = {};
  106. if (config.packageManager && config.packageManager !== 'default') {
  107. newConfig['packageManager'] = config.packageManager;
  108. }
  109. if (config.warnings) {
  110. if (config.warnings.versionMismatch !== undefined) {
  111. newConfig.warnings = {
  112. ...(newConfig.warnings || {}),
  113. ...{ versionMismatch: config.warnings.versionMismatch },
  114. };
  115. }
  116. }
  117. return Object.getOwnPropertyNames(newConfig).length == 0 ? null : newConfig;
  118. }
  119. function extractSchematicsConfig(config) {
  120. let collectionName = '@schematics/angular';
  121. if (!config || !config.defaults) {
  122. return null;
  123. }
  124. // const configDefaults = config.defaults;
  125. if (config.defaults && config.defaults.schematics && config.defaults.schematics.collection) {
  126. collectionName = config.defaults.schematics.collection;
  127. }
  128. /**
  129. * For each schematic
  130. * - get the config
  131. * - filter one's without config
  132. * - combine them into an object
  133. */
  134. // tslint:disable-next-line:no-any
  135. const schematicConfigs = ['class', 'component', 'directive', 'guard',
  136. 'interface', 'module', 'pipe', 'service']
  137. .map(schematicName => {
  138. // tslint:disable-next-line:no-any
  139. const schematicDefaults = config.defaults[schematicName] || null;
  140. return {
  141. schematicName,
  142. config: schematicDefaults,
  143. };
  144. })
  145. .filter(schematic => schematic.config !== null)
  146. .reduce((all, schematic) => {
  147. all[collectionName + ':' + schematic.schematicName] = schematic.config;
  148. return all;
  149. }, {});
  150. const componentUpdate = {};
  151. componentUpdate.prefix = '';
  152. const componentKey = collectionName + ':component';
  153. const directiveKey = collectionName + ':directive';
  154. if (!schematicConfigs[componentKey]) {
  155. schematicConfigs[componentKey] = {};
  156. }
  157. if (!schematicConfigs[directiveKey]) {
  158. schematicConfigs[directiveKey] = {};
  159. }
  160. if (config.apps && config.apps[0]) {
  161. schematicConfigs[componentKey].prefix = config.apps[0].prefix;
  162. schematicConfigs[directiveKey].prefix = config.apps[0].prefix;
  163. }
  164. if (config.defaults) {
  165. schematicConfigs[componentKey].styleext = config.defaults.styleExt;
  166. }
  167. return schematicConfigs;
  168. }
  169. function extractTargetsConfig(_config) {
  170. return null;
  171. }
  172. // This function is too big, but also really hard to refactor properly as the whole config
  173. // depends on all parts of the config.
  174. // tslint:disable-next-line:no-big-function
  175. function extractProjectsConfig(config, tree, logger) {
  176. const builderPackage = '@angular-devkit/build-angular';
  177. const defaultAppNamePrefix = getDefaultAppNamePrefix(config);
  178. const buildDefaults = config.defaults && config.defaults.build
  179. ? {
  180. sourceMap: config.defaults.build.sourcemaps,
  181. progress: config.defaults.build.progress,
  182. poll: config.defaults.build.poll,
  183. deleteOutputPath: config.defaults.build.deleteOutputPath,
  184. preserveSymlinks: config.defaults.build.preserveSymlinks,
  185. showCircularDependencies: config.defaults.build.showCircularDependencies,
  186. commonChunk: config.defaults.build.commonChunk,
  187. namedChunks: config.defaults.build.namedChunks,
  188. }
  189. : {};
  190. const serveDefaults = config.defaults && config.defaults.serve
  191. ? {
  192. port: config.defaults.serve.port,
  193. host: config.defaults.serve.host,
  194. ssl: config.defaults.serve.ssl,
  195. sslKey: config.defaults.serve.sslKey,
  196. sslCert: config.defaults.serve.sslCert,
  197. proxyConfig: config.defaults.serve.proxyConfig,
  198. }
  199. : {};
  200. const apps = config.apps || [];
  201. // convert the apps to projects
  202. const browserApps = apps.filter(app => app.platform !== 'server');
  203. const serverApps = apps.filter(app => app.platform === 'server');
  204. const projectMap = browserApps
  205. // This function is too big, but also really hard to refactor properly as the whole config
  206. // depends on all parts of the config.
  207. // tslint:disable-next-line:no-big-function
  208. .map((app, idx) => {
  209. const defaultAppName = idx === 0 ? defaultAppNamePrefix : `${defaultAppNamePrefix}${idx}`;
  210. const name = app.name || defaultAppName;
  211. const outDir = app.outDir || defaults.outDir;
  212. const appRoot = app.root || defaults.appRoot;
  213. function _mapAssets(asset) {
  214. if (typeof asset === 'string') {
  215. return core_1.normalize(appRoot + '/' + asset);
  216. }
  217. else {
  218. if (asset.allowOutsideOutDir) {
  219. logger.warn(core_1.tags.oneLine `
  220. Asset with input '${asset.input}' was not migrated because it
  221. uses the 'allowOutsideOutDir' option which is not supported in Angular CLI 6.
  222. `);
  223. return null;
  224. }
  225. else if (asset.output) {
  226. return {
  227. glob: asset.glob,
  228. input: core_1.normalize(appRoot + '/' + asset.input),
  229. output: core_1.normalize('/' + asset.output),
  230. };
  231. }
  232. else {
  233. return {
  234. glob: asset.glob,
  235. input: core_1.normalize(appRoot + '/' + asset.input),
  236. output: '/',
  237. };
  238. }
  239. }
  240. }
  241. function _buildConfigurations() {
  242. const source = app.environmentSource;
  243. const environments = app.environments;
  244. const serviceWorker = app.serviceWorker;
  245. const productionPartial = {
  246. optimization: true,
  247. outputHashing: 'all',
  248. sourceMap: false,
  249. extractCss: true,
  250. namedChunks: false,
  251. aot: true,
  252. extractLicenses: true,
  253. vendorChunk: false,
  254. buildOptimizer: true,
  255. ...(serviceWorker ? { serviceWorker: true, ngswConfigPath: 'src/ngsw-config.json' } : {}),
  256. ...(app.budgets ? { budgets: app.budgets } : {}),
  257. };
  258. if (!environments) {
  259. return { production: productionPartial };
  260. }
  261. const configurations = Object.keys(environments).reduce((acc, environment) => {
  262. if (source === environments[environment]) {
  263. return acc;
  264. }
  265. let isProduction = false;
  266. const environmentContent = tree.read(app.root + '/' + environments[environment]);
  267. if (environmentContent) {
  268. isProduction = !!environmentContent.toString('utf-8')
  269. // Allow for `production: true` or `production = true`. Best we can do to guess.
  270. .match(/production['"]?\s*[:=]\s*true/);
  271. }
  272. let configurationName;
  273. // We used to use `prod` by default as the key, instead we now use the full word.
  274. // Try not to override the production key if it's there.
  275. if (environment == 'prod' && !environments['production'] && isProduction) {
  276. configurationName = 'production';
  277. }
  278. else {
  279. configurationName = environment;
  280. }
  281. acc[configurationName] = {
  282. ...(isProduction ? productionPartial : {}),
  283. fileReplacements: [
  284. {
  285. replace: `${app.root}/${source}`,
  286. with: `${app.root}/${environments[environment]}`,
  287. },
  288. ],
  289. };
  290. return acc;
  291. }, {});
  292. if (!configurations['production']) {
  293. configurations['production'] = { ...productionPartial };
  294. }
  295. return configurations;
  296. }
  297. function _serveConfigurations() {
  298. const environments = app.environments;
  299. if (!environments) {
  300. return {};
  301. }
  302. if (!targets) {
  303. throw new Error();
  304. }
  305. const configurations = targets.build.configurations;
  306. return Object.keys(configurations).reduce((acc, environment) => {
  307. acc[environment] = { browserTarget: `${name}:build:${environment}` };
  308. return acc;
  309. }, {});
  310. }
  311. function _extraEntryMapper(extraEntry) {
  312. let entry;
  313. if (typeof extraEntry === 'string') {
  314. entry = core_1.join(app.root, extraEntry);
  315. }
  316. else {
  317. const input = core_1.join(app.root, extraEntry.input || '');
  318. entry = { input, lazy: extraEntry.lazy };
  319. if (extraEntry.output) {
  320. entry.bundleName = extraEntry.output;
  321. }
  322. }
  323. return entry;
  324. }
  325. const projectRoot = core_1.join(core_1.normalize(appRoot), '..');
  326. const project = {
  327. root: projectRoot,
  328. sourceRoot: appRoot,
  329. projectType: 'application',
  330. };
  331. const targets = {};
  332. project.architect = targets;
  333. // Browser target
  334. const buildOptions = {
  335. // Make outputPath relative to root.
  336. outputPath: outDir,
  337. index: `${appRoot}/${app.index || defaults.index}`,
  338. main: `${appRoot}/${app.main || defaults.main}`,
  339. tsConfig: `${appRoot}/${app.tsconfig || defaults.tsConfig}`,
  340. ...(app.baseHref ? { baseHref: app.baseHref } : {}),
  341. ...buildDefaults,
  342. };
  343. if (app.polyfills) {
  344. buildOptions.polyfills = appRoot + '/' + app.polyfills;
  345. }
  346. if (app.stylePreprocessorOptions
  347. && app.stylePreprocessorOptions.includePaths
  348. && Array.isArray(app.stylePreprocessorOptions.includePaths)
  349. && app.stylePreprocessorOptions.includePaths.length > 0) {
  350. buildOptions.stylePreprocessorOptions = {
  351. includePaths: app.stylePreprocessorOptions.includePaths
  352. .map(includePath => core_1.join(app.root, includePath)),
  353. };
  354. }
  355. buildOptions.assets = (app.assets || []).map(_mapAssets).filter(x => !!x);
  356. buildOptions.styles = (app.styles || []).map(_extraEntryMapper);
  357. buildOptions.scripts = (app.scripts || []).map(_extraEntryMapper);
  358. targets.build = {
  359. builder: `${builderPackage}:browser`,
  360. options: buildOptions,
  361. configurations: _buildConfigurations(),
  362. };
  363. // Serve target
  364. const serveOptions = {
  365. browserTarget: `${name}:build`,
  366. ...serveDefaults,
  367. };
  368. targets.serve = {
  369. builder: `${builderPackage}:dev-server`,
  370. options: serveOptions,
  371. configurations: _serveConfigurations(),
  372. };
  373. // Extract target
  374. const extractI18nOptions = { browserTarget: `${name}:build` };
  375. targets['extract-i18n'] = {
  376. builder: `${builderPackage}:extract-i18n`,
  377. options: extractI18nOptions,
  378. };
  379. const karmaConfig = config.test && config.test.karma
  380. ? config.test.karma.config || ''
  381. : '';
  382. // Test target
  383. const testOptions = {
  384. main: appRoot + '/' + app.test || defaults.test,
  385. // Make karmaConfig relative to root.
  386. karmaConfig,
  387. };
  388. if (app.polyfills) {
  389. testOptions.polyfills = appRoot + '/' + app.polyfills;
  390. }
  391. if (app.testTsconfig) {
  392. testOptions.tsConfig = appRoot + '/' + app.testTsconfig;
  393. }
  394. const codeCoverageExclude = config.test
  395. && config.test.codeCoverage
  396. && config.test.codeCoverage.exclude;
  397. if (codeCoverageExclude) {
  398. testOptions.codeCoverageExclude = codeCoverageExclude;
  399. }
  400. testOptions.scripts = (app.scripts || []).map(_extraEntryMapper);
  401. testOptions.styles = (app.styles || []).map(_extraEntryMapper);
  402. testOptions.assets = (app.assets || []).map(_mapAssets).filter(x => !!x);
  403. if (karmaConfig) {
  404. targets.test = {
  405. builder: `${builderPackage}:karma`,
  406. options: testOptions,
  407. };
  408. }
  409. const tsConfigs = [];
  410. const excludes = [];
  411. let warnForLint = false;
  412. if (config && config.lint && Array.isArray(config.lint)) {
  413. config.lint.forEach(lint => {
  414. if (lint.project) {
  415. tsConfigs.push(lint.project);
  416. }
  417. else {
  418. warnForLint = true;
  419. }
  420. if (lint.exclude) {
  421. if (typeof lint.exclude === 'string') {
  422. excludes.push(lint.exclude);
  423. }
  424. else {
  425. lint.exclude.forEach(ex => excludes.push(ex));
  426. }
  427. }
  428. });
  429. }
  430. if (warnForLint) {
  431. logger.warn(`
  432. Lint without 'project' was not migrated which is not supported in Angular CLI 6.
  433. `);
  434. }
  435. const removeDupes = (items) => items.reduce((newItems, item) => {
  436. if (newItems.indexOf(item) === -1) {
  437. newItems.push(item);
  438. }
  439. return newItems;
  440. }, []);
  441. // Tslint target
  442. const lintOptions = {
  443. tsConfig: removeDupes(tsConfigs).filter(t => t.indexOf('e2e') === -1),
  444. exclude: removeDupes(excludes),
  445. };
  446. targets.lint = {
  447. builder: `${builderPackage}:tslint`,
  448. options: lintOptions,
  449. };
  450. // server target
  451. const serverApp = serverApps
  452. .filter(serverApp => app.root === serverApp.root && app.index === serverApp.index)[0];
  453. if (serverApp) {
  454. const serverOptions = {
  455. outputPath: serverApp.outDir || defaults.serverOutDir,
  456. main: `${appRoot}/${serverApp.main || defaults.serverMain}`,
  457. tsConfig: `${appRoot}/${serverApp.tsconfig || defaults.serverTsConfig}`,
  458. };
  459. const serverTarget = {
  460. builder: '@angular-devkit/build-angular:server',
  461. options: serverOptions,
  462. };
  463. targets.server = serverTarget;
  464. }
  465. const e2eProject = {
  466. root: core_1.join(projectRoot, 'e2e'),
  467. sourceRoot: core_1.join(projectRoot, 'e2e'),
  468. projectType: 'application',
  469. };
  470. const e2eTargets = {};
  471. const protractorConfig = config && config.e2e && config.e2e.protractor && config.e2e.protractor.config
  472. ? config.e2e.protractor.config
  473. : '';
  474. const e2eOptions = {
  475. protractorConfig: protractorConfig,
  476. devServerTarget: `${name}:serve`,
  477. };
  478. const e2eTarget = {
  479. builder: `${builderPackage}:protractor`,
  480. options: e2eOptions,
  481. };
  482. e2eTargets.e2e = e2eTarget;
  483. const e2eLintOptions = {
  484. tsConfig: removeDupes(tsConfigs).filter(t => t.indexOf('e2e') !== -1),
  485. exclude: removeDupes(excludes),
  486. };
  487. const e2eLintTarget = {
  488. builder: `${builderPackage}:tslint`,
  489. options: e2eLintOptions,
  490. };
  491. e2eTargets.lint = e2eLintTarget;
  492. if (protractorConfig) {
  493. e2eProject.architect = e2eTargets;
  494. }
  495. return { name, project, e2eProject };
  496. })
  497. .reduce((projects, mappedApp) => {
  498. const { name, project, e2eProject } = mappedApp;
  499. projects[name] = project;
  500. projects[name + '-e2e'] = e2eProject;
  501. return projects;
  502. }, {});
  503. return projectMap;
  504. }
  505. function getDefaultAppNamePrefix(config) {
  506. let defaultAppNamePrefix = 'app';
  507. if (config.project && config.project.name) {
  508. defaultAppNamePrefix = config.project.name;
  509. }
  510. return defaultAppNamePrefix;
  511. }
  512. function extractDefaultProject(config) {
  513. if (config.apps && config.apps[0]) {
  514. const app = config.apps[0];
  515. const defaultAppName = getDefaultAppNamePrefix(config);
  516. const name = app.name || defaultAppName;
  517. return name;
  518. }
  519. return null;
  520. }
  521. function updateSpecTsConfig(config) {
  522. return (host, context) => {
  523. const apps = config.apps || [];
  524. apps.forEach((app, idx) => {
  525. const testTsConfig = app.testTsconfig || defaults.testTsConfig;
  526. const tsSpecConfigPath = core_1.join(core_1.normalize(app.root || ''), testTsConfig);
  527. const buffer = host.read(tsSpecConfigPath);
  528. if (!buffer) {
  529. return;
  530. }
  531. const tsCfgAst = core_1.parseJsonAst(buffer.toString(), core_1.JsonParseMode.Loose);
  532. if (tsCfgAst.kind != 'object') {
  533. throw new schematics_1.SchematicsException('Invalid tsconfig. Was expecting an object');
  534. }
  535. const filesAstNode = json_utils_1.findPropertyInAstObject(tsCfgAst, 'files');
  536. if (filesAstNode && filesAstNode.kind != 'array') {
  537. throw new schematics_1.SchematicsException('Invalid tsconfig "files" property; expected an array.');
  538. }
  539. const recorder = host.beginUpdate(tsSpecConfigPath);
  540. const polyfills = app.polyfills || defaults.polyfills;
  541. if (!filesAstNode) {
  542. // Do nothing if the files array does not exist. This means exclude or include are
  543. // set and we shouldn't mess with that.
  544. }
  545. else {
  546. if (filesAstNode.value.indexOf(polyfills) == -1) {
  547. json_utils_1.appendValueInAstArray(recorder, filesAstNode, polyfills);
  548. }
  549. }
  550. host.commitUpdate(recorder);
  551. });
  552. };
  553. }
  554. function updatePackageJson(config) {
  555. return (host, context) => {
  556. const dependency = {
  557. type: dependencies_1.NodeDependencyType.Dev,
  558. name: '@angular-devkit/build-angular',
  559. version: latest_versions_1.latestVersions.DevkitBuildAngular,
  560. overwrite: true,
  561. };
  562. dependencies_1.addPackageJsonDependency(host, dependency);
  563. context.addTask(new tasks_1.NodePackageInstallTask({
  564. packageManager: config.packageManager === 'default' ? undefined : config.packageManager,
  565. }));
  566. return host;
  567. };
  568. }
  569. function updateTsLintConfig() {
  570. return (host, context) => {
  571. const tsLintPath = '/tslint.json';
  572. const buffer = host.read(tsLintPath);
  573. if (!buffer) {
  574. return host;
  575. }
  576. const tsCfgAst = core_1.parseJsonAst(buffer.toString(), core_1.JsonParseMode.Loose);
  577. if (tsCfgAst.kind != 'object') {
  578. return host;
  579. }
  580. const rulesNode = json_utils_1.findPropertyInAstObject(tsCfgAst, 'rules');
  581. if (!rulesNode || rulesNode.kind != 'object') {
  582. return host;
  583. }
  584. const importBlacklistNode = json_utils_1.findPropertyInAstObject(rulesNode, 'import-blacklist');
  585. if (!importBlacklistNode || importBlacklistNode.kind != 'array') {
  586. return host;
  587. }
  588. const recorder = host.beginUpdate(tsLintPath);
  589. for (let i = 0; i < importBlacklistNode.elements.length; i++) {
  590. const element = importBlacklistNode.elements[i];
  591. if (element.kind == 'string' && element.value == 'rxjs') {
  592. const { start, end } = element;
  593. // Remove this element.
  594. if (i == importBlacklistNode.elements.length - 1) {
  595. // Last element.
  596. if (i > 0) {
  597. // Not first, there's a comma to remove before.
  598. const previous = importBlacklistNode.elements[i - 1];
  599. recorder.remove(previous.end.offset, end.offset - previous.end.offset);
  600. }
  601. else {
  602. // Only element, just remove the whole rule.
  603. const { start, end } = importBlacklistNode;
  604. recorder.remove(start.offset, end.offset - start.offset);
  605. recorder.insertLeft(start.offset, '[]');
  606. }
  607. }
  608. else {
  609. // Middle, just remove the whole node (up to next node start).
  610. const next = importBlacklistNode.elements[i + 1];
  611. recorder.remove(start.offset, next.start.offset - start.offset);
  612. }
  613. }
  614. }
  615. host.commitUpdate(recorder);
  616. return host;
  617. };
  618. }
  619. function updateRootTsConfig() {
  620. return (host, context) => {
  621. const tsConfigPath = '/tsconfig.json';
  622. const buffer = host.read(tsConfigPath);
  623. if (!buffer) {
  624. return;
  625. }
  626. const tsCfgAst = core_1.parseJsonAst(buffer.toString(), core_1.JsonParseMode.Loose);
  627. if (tsCfgAst.kind !== 'object') {
  628. throw new schematics_1.SchematicsException('Invalid root tsconfig. Was expecting an object');
  629. }
  630. const compilerOptionsAstNode = json_utils_1.findPropertyInAstObject(tsCfgAst, 'compilerOptions');
  631. if (!compilerOptionsAstNode || compilerOptionsAstNode.kind != 'object') {
  632. throw new schematics_1.SchematicsException('Invalid root tsconfig "compilerOptions" property; expected an object.');
  633. }
  634. if (json_utils_1.findPropertyInAstObject(compilerOptionsAstNode, 'baseUrl') &&
  635. json_utils_1.findPropertyInAstObject(compilerOptionsAstNode, 'module')) {
  636. return host;
  637. }
  638. const compilerOptions = compilerOptionsAstNode.value;
  639. const { baseUrl = './', module = 'es2015' } = compilerOptions;
  640. const validBaseUrl = ['./', '', '.'];
  641. if (!validBaseUrl.includes(baseUrl)) {
  642. const formattedBaseUrl = validBaseUrl.map(x => `'${x}'`).join(', ');
  643. context.logger.warn(core_1.tags.oneLine `Root tsconfig option 'baseUrl' is not one of: ${formattedBaseUrl}.
  644. This might cause unexpected behaviour when generating libraries.`);
  645. }
  646. if (module !== 'es2015') {
  647. context.logger.warn(`Root tsconfig option 'module' is not 'es2015'. This might cause unexpected behaviour.`);
  648. }
  649. compilerOptions.module = module;
  650. compilerOptions.baseUrl = baseUrl;
  651. host.overwrite(tsConfigPath, JSON.stringify(tsCfgAst.value, null, 2));
  652. return host;
  653. };
  654. }
  655. function default_1() {
  656. return (host, context) => {
  657. if (host.exists('/.angular.json') || host.exists('/angular.json')) {
  658. context.logger.info('Found a modern configuration file. Nothing to be done.');
  659. return host;
  660. }
  661. const configPath = getConfigPath(host);
  662. const configBuffer = host.read(core_1.normalize(configPath));
  663. if (configBuffer == null) {
  664. throw new schematics_1.SchematicsException(`Could not find configuration file (${configPath})`);
  665. }
  666. const config = core_1.parseJson(configBuffer.toString(), core_1.JsonParseMode.Loose);
  667. if (typeof config != 'object' || Array.isArray(config) || config === null) {
  668. throw new schematics_1.SchematicsException('Invalid angular-cli.json configuration; expected an object.');
  669. }
  670. return schematics_1.chain([
  671. migrateKarmaConfiguration(config),
  672. migrateConfiguration(config, context.logger),
  673. updateSpecTsConfig(config),
  674. updatePackageJson(config),
  675. updateRootTsConfig(),
  676. updateTsLintConfig(),
  677. (host, context) => {
  678. context.logger.warn(core_1.tags.oneLine `Some configuration options have been changed,
  679. please make sure to update any npm scripts which you may have modified.`);
  680. return host;
  681. },
  682. ]);
  683. };
  684. }
  685. exports.default = default_1;