UNPKG

@yolkai/nx-schematics

Version:

Extensible Dev Tools for Monorepos: Schematics

228 lines (227 loc) 10.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const schematics_1 = require("@angular-devkit/schematics"); const ts = require("typescript"); const nx_workspace_1 = require("@yolkai/nx-workspace"); const ast_utils_1 = require("@yolkai/nx-workspace/src/utils/ast-utils"); const addExtensionRecommendations = nx_workspace_1.updateJsonInTree('.vscode/extensions.json', (json) => { json.recommendations = json.recommendations || []; [ 'nrwl.angular-console', 'angular.ng-template', 'esbenp.prettier-vscode' ].forEach(extension => { if (!json.recommendations.includes(extension)) { json.recommendations.push(extension); } }); return json; }); function addItemToImport(path, sourceFile, printer, importStatement, symbol) { const newImport = ts.createImportDeclaration(importStatement.decorators, importStatement.modifiers, ts.createImportClause(importStatement.importClause.name, ts.createNamedImports([ ...importStatement.importClause.namedBindings .elements, ts.createImportSpecifier(undefined, ts.createIdentifier(symbol)) ])), importStatement.moduleSpecifier); return new ast_utils_1.ReplaceChange(path, importStatement.getStart(sourceFile), importStatement.getText(sourceFile), printer.printNode(ts.EmitHint.Unspecified, newImport, sourceFile)); } function isEffectDecorator(decorator) { return (ts.isCallExpression(decorator.expression) && ts.isIdentifier(decorator.expression.expression) && decorator.expression.expression.text === 'Effect'); } function getImport(sourceFile, path, symbol) { return sourceFile.statements .filter(ts.isImportDeclaration) .filter(statement => statement.moduleSpecifier.getText(sourceFile).includes(path)) .find(statement => { if (!ts.isNamedImports(statement.importClause.namedBindings)) { return false; } return statement.importClause.namedBindings.elements.some(element => element.getText(sourceFile) === symbol); }); } function updateOfTypeCode(path, sourceFile) { const effectsImport = getImport(sourceFile, '@ngrx/effects', 'Effect'); if (!effectsImport) { return []; } const effects = []; const changes = []; const printer = ts.createPrinter(); sourceFile.statements .filter(ts.isClassDeclaration) .map(clazz => clazz.members .filter(ts.isPropertyDeclaration) .filter(member => member.decorators && member.decorators.some(isEffectDecorator))) .forEach(properties => { effects.push(...properties); }); effects.forEach(effect => { if (ts.isCallExpression(effect.initializer) && ts.isPropertyAccessExpression(effect.initializer.expression) && effect.initializer.expression.name.text === 'pipe' && ts.isCallExpression(effect.initializer.expression.expression) && ts.isPropertyAccessExpression(effect.initializer.expression.expression.expression) && effect.initializer.expression.expression.expression.name.text === 'ofType') { const originalText = effect.initializer.getText(sourceFile); const ofTypeExpression = ts.createCall(ts.createIdentifier('ofType'), effect.initializer.expression.expression.typeArguments, effect.initializer.expression.expression.arguments); const node = ts.createCall(ts.createPropertyAccess(effect.initializer.expression.expression.expression.expression, 'pipe'), effect.initializer.typeArguments, ts.createNodeArray([ ofTypeExpression, ...effect.initializer.arguments ])); const newEffect = printer.printNode(ts.EmitHint.Expression, node, sourceFile); const change = new ast_utils_1.ReplaceChange(path, effect.initializer.getStart(sourceFile), originalText, newEffect); changes.push(change); } }); if (changes.length > 0) { changes.unshift(addItemToImport(path, sourceFile, printer, effectsImport, 'ofType')); } return changes; } function getConstructor(classDeclaration) { return classDeclaration.members.find(ts.isConstructorDeclaration); } function getStoreProperty(sourceFile, constructor) { const storeParameter = constructor.parameters.find(parameter => parameter.type && parameter.type.getText(sourceFile).includes('Store')); return storeParameter ? storeParameter.name.getText(sourceFile) : null; } function updateSelectorCode(path, sourceFile) { const storeImport = getImport(sourceFile, '@ngrx/store', 'Store'); if (!storeImport) { return []; } const changes = []; const printer = ts.createPrinter(); sourceFile.statements .filter(ts.isClassDeclaration) .forEach(classDeclaration => { const constructor = getConstructor(classDeclaration); if (!constructor) { return; } const storeProperty = getStoreProperty(sourceFile, constructor); ast_utils_1.getSourceNodes(sourceFile).forEach(node => { if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isPropertyAccessExpression(node.expression.expression) && ts.isIdentifier(node.expression.name) && ts.isIdentifier(node.expression.expression.name) && node.expression.name.getText(sourceFile) === 'select' && node.expression.expression.name.getText(sourceFile) === storeProperty && node.expression.expression.expression.kind === ts.SyntaxKind.ThisKeyword) { const newExpression = ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier('this'), ts.createIdentifier(storeProperty)), ts.createIdentifier('pipe')), [], [ ts.createCall(ts.createIdentifier('select'), node.typeArguments, node.arguments) ]); const newNode = printer.printNode(ts.EmitHint.Expression, newExpression, sourceFile); changes.push(new ast_utils_1.ReplaceChange(path, node.getStart(sourceFile), node.getText(sourceFile), newNode)); } }); }); if (changes.length > 0) { changes.unshift(addItemToImport(path, sourceFile, printer, storeImport, 'select')); } return changes; } function migrateNgrx(host) { const ngrxVersion = nx_workspace_1.readJsonInTree(host, 'package.json').dependencies['@ngrx/store']; if (ngrxVersion && !(ngrxVersion.startsWith('6.') || ngrxVersion.startsWith('~6.') || ngrxVersion.startsWith('^6.'))) { return host; } host.visit(path => { if (!path.endsWith('.ts')) { return; } let sourceFile = ts.createSourceFile(path, host.read(path).toString(), ts.ScriptTarget.Latest); if (sourceFile.isDeclarationFile) { return; } nx_workspace_1.insert(host, path, updateOfTypeCode(path, sourceFile)); sourceFile = ts.createSourceFile(path, host.read(path).toString(), ts.ScriptTarget.Latest); nx_workspace_1.insert(host, path, updateSelectorCode(path, sourceFile)); sourceFile = ts.createSourceFile(path, host.read(path).toString(), ts.ScriptTarget.Latest); nx_workspace_1.insert(host, path, cleanUpDoublePipes(path, sourceFile)); }); } function cleanUpDoublePipes(path, sourceFile) { const changes = []; const printer = ts.createPrinter(); ast_utils_1.getSourceNodes(sourceFile).forEach(node => { if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isCallExpression(node.expression.expression) && ts.isPropertyAccessExpression(node.expression.expression.expression) && node.expression.name.text === 'pipe' && node.expression.expression.expression.name.text === 'pipe') { const singlePipe = ts.createCall(node.expression.expression.expression, node.typeArguments, [...node.expression.expression.arguments, ...node.arguments]); changes.push(new ast_utils_1.ReplaceChange(path, node.getStart(sourceFile), node.getText(sourceFile), printer.printNode(ts.EmitHint.Expression, singlePipe, sourceFile))); } }); return changes; } const updateNgrx = nx_workspace_1.updateJsonInTree('package.json', json => { json.devDependencies = json.devDependencies || {}; json.dependencies = json.dependencies || {}; json.dependencies = Object.assign({}, json.dependencies, { '@ngrx/effects': '7.2.0', '@ngrx/router-store': '7.2.0', '@ngrx/store': '7.2.0' }); json.devDependencies = Object.assign({}, json.devDependencies, { '@ngrx/schematics': '7.2.0', '@ngrx/store-devtools': '7.2.0' }); return json; }); const addDotEnv = nx_workspace_1.updateJsonInTree('package.json', json => { json.devDependencies = json.devDependencies || {}; json.devDependencies = Object.assign({}, json.devDependencies, { dotenv: '6.2.0' }); return json; }); const setDefaults = nx_workspace_1.updateWorkspaceInTree(json => { if (!json.schematics) { json.schematics = {}; } if (!json.schematics['@yolkai/nx-schematics:library']) { json.schematics['@yolkai/nx-schematics:library'] = {}; } if (!json.schematics['@yolkai/nx-schematics:library'].unitTestRunner) { json.schematics['@yolkai/nx-schematics:library'].unitTestRunner = 'karma'; } if (!json.schematics['@yolkai/nx-schematics:application']) { json.schematics['@yolkai/nx-schematics:application'] = {}; } if (!json.schematics['@yolkai/nx-schematics:application'].unitTestRunner) { json.schematics['@yolkai/nx-schematics:application'].unitTestRunner = 'karma'; } if (!json.schematics['@yolkai/nx-schematics:application'].e2eTestRunner) { json.schematics['@yolkai/nx-schematics:application'].e2eTestRunner = 'protractor'; } if (!json.schematics['@yolkai/nx-schematics:node-application']) { json.schematics['@yolkai/nx-schematics:node-application'] = {}; } if (!json.schematics['@yolkai/nx-schematics:node-application'].framework) { json.schematics['@yolkai/nx-schematics:node-application'].framework = 'express'; } return json; }); const updateAngularCLI = schematics_1.chain([ nx_workspace_1.addUpdateTask('@angular/cli', '7.3.1'), nx_workspace_1.addDepsToPackageJson({}, { '@angular-devkit/build-angular': '~0.13.1' }) ]); function default_1() { return schematics_1.chain([ addExtensionRecommendations, addDotEnv, updateAngularCLI, migrateNgrx, updateNgrx, setDefaults, nx_workspace_1.formatFiles() ]); } exports.default = default_1;