UNPKG

@spartacus/schematics

Version:
723 lines 36.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getServerTsPath = exports.getLineFromTSFile = exports.getMetadataProperty = exports.findMultiLevelNodesByTextAndKind = exports.renameIdentifierNode = exports.insertCommentAboveImportIdentifier = exports.insertCommentAboveIdentifier = exports.insertCommentAboveConfigProperty = exports.buildSpartacusComment = exports.injectService = exports.removeImport = exports.removeInjectImports = exports.shouldRemoveDecorator = exports.removeConstructorParam = exports.addConstructorParam = exports.isInheriting = exports.isCandidateForConstructorDeprecation = exports.defineProperty = exports.findConstructor = exports.commitChanges = exports.insertHtmlComment = exports.insertComponentSelectorComment = exports.getHtmlFiles = exports.getPathResultsForFile = exports.getIndexHtmlPath = exports.getAllTsSourceFiles = exports.getTsSourceFile = exports.InsertDirection = void 0; const core_1 = require("@angular-devkit/core"); const schematics_1 = require("@angular-devkit/schematics"); const compiler_1 = require("@angular/compiler"); const ast_utils_1 = require("@schematics/angular/utility/ast-utils"); const change_1 = require("@schematics/angular/utility/change"); const typescript_1 = __importDefault(require("typescript")); const constants_1 = require("../constants"); const workspace_utils_1 = require("./workspace-utils"); var InsertDirection; (function (InsertDirection) { InsertDirection[InsertDirection["LEFT"] = 0] = "LEFT"; InsertDirection[InsertDirection["RIGHT"] = 1] = "RIGHT"; })(InsertDirection = exports.InsertDirection || (exports.InsertDirection = {})); function getTsSourceFile(tree, path) { const buffer = tree.read(path); if (!buffer) { throw new schematics_1.SchematicsException(`Could not read file (${path}).`); } const content = buffer.toString(constants_1.UTF_8); const source = typescript_1.default.createSourceFile(path, content, typescript_1.default.ScriptTarget.Latest, true); return source; } exports.getTsSourceFile = getTsSourceFile; function getAllTsSourceFiles(tree, basePath) { const results = []; tree.getDir(basePath).visit((filePath) => { if (filePath.endsWith('.ts')) { results.push(filePath); } }); return results.map((f) => getTsSourceFile(tree, f)); } exports.getAllTsSourceFiles = getAllTsSourceFiles; function getIndexHtmlPath(tree) { var _a, _b, _c, _d; const projectName = workspace_utils_1.getDefaultProjectNameFromWorkspace(tree); const angularJson = workspace_utils_1.getAngularJsonFile(tree); const indexHtml = (_d = (_c = (_b = (_a = angularJson.projects[projectName]) === null || _a === void 0 ? void 0 : _a.architect) === null || _b === void 0 ? void 0 : _b.build) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.index; if (!indexHtml) { throw new schematics_1.SchematicsException('"index.html" file not found.'); } return indexHtml; } exports.getIndexHtmlPath = getIndexHtmlPath; function getPathResultsForFile(tree, file, directory) { const results = []; const dir = directory || '/'; tree.getDir(dir).visit((filePath) => { if (filePath.endsWith(file)) { results.push(filePath); } }); return results; } exports.getPathResultsForFile = getPathResultsForFile; function getHtmlFiles(tree, fileName = '.html', directory) { return getPathResultsForFile(tree, fileName || '.html', directory); } exports.getHtmlFiles = getHtmlFiles; function insertComponentSelectorComment(content, componentSelector, componentProperty) { const selector = buildSelector(componentSelector); const comment = buildHtmlComment(componentProperty.comment); let index = 0; let newContent = content; while (true) { index = getTextPosition(newContent, selector, index); if (index == null) { break; } newContent = newContent.slice(0, index) + comment + newContent.slice(index); index += comment.length + componentSelector.length; } return newContent; } exports.insertComponentSelectorComment = insertComponentSelectorComment; function getTextPosition(content, text, startingPosition = 0) { const index = content.indexOf(text, startingPosition); return index !== -1 ? index : undefined; } function buildSelector(selector) { return `<${selector}`; } function visitHtmlNodesRecursively(nodes, propertyName, resultingElements = [], parentElement) { nodes.forEach((node) => { if (node instanceof compiler_1.Attribute && parentElement) { if (node.name.includes(propertyName) || node.value.includes(propertyName)) { resultingElements.push(parentElement); } } if (node instanceof compiler_1.Element) { visitHtmlNodesRecursively(node.attrs, propertyName, resultingElements, node); visitHtmlNodesRecursively(node.children, propertyName, resultingElements, node); } }); } function insertHtmlComment(content, componentProperty) { const comment = buildHtmlComment(componentProperty.comment); const result = new compiler_1.HtmlParser().parse(content, ''); const resultingElements = []; visitHtmlNodesRecursively(result.rootNodes, componentProperty.name, resultingElements); resultingElements .map((node) => node.sourceSpan.start.line) .forEach((line, i) => { const split = content.split('\n'); split.splice(line + i, 0, comment); content = split.join('\n'); }); return content; } exports.insertHtmlComment = insertHtmlComment; function buildHtmlComment(commentText) { return `<!-- ${constants_1.TODO_SPARTACUS} ${commentText} -->`; } function commitChanges(host, path, changes, insertDirection = InsertDirection.RIGHT) { if (!changes || changes.length === 0) { return; } const recorder = host.beginUpdate(path); changes.forEach((change) => { if (change instanceof change_1.InsertChange) { const pos = change.pos; const toAdd = change.toAdd; if (insertDirection === InsertDirection.LEFT) { recorder.insertLeft(pos, toAdd); } else { recorder.insertRight(pos, toAdd); } } else if (change instanceof change_1.RemoveChange) { const pos = change['pos']; const length = change['toRemove'].length; recorder.remove(pos, length); } else if (change instanceof change_1.NoopChange) { // nothing to do here... } else { const pos = change['pos']; const oldText = change['oldText']; const newText = change['newText']; recorder.remove(pos, oldText.length); if (insertDirection === InsertDirection.LEFT) { recorder.insertLeft(pos, newText); } else { recorder.insertRight(pos, newText); } } }); host.commitUpdate(recorder); } exports.commitChanges = commitChanges; function findConstructor(nodes) { return nodes.find((n) => n.kind === typescript_1.default.SyntaxKind.Constructor); } exports.findConstructor = findConstructor; function defineProperty(nodes, path, toAdd) { const constructorNode = findConstructor(nodes); if (!constructorNode) { throw new schematics_1.SchematicsException(`No constructor found in ${path}.`); } return new change_1.InsertChange(path, constructorNode.pos + 1, toAdd); } exports.defineProperty = defineProperty; /** * * Method performs the following checks on the provided `source` file: * - is the file inheriting the provided `constructorDeprecation.class` * - is the `constructorDeprecation.class` imported from the specified `constructorDeprecation.importPath` * - is the file importing all the provided `parameterClassTypes` from the expected import path * - does the provided file contain a constructor * - does the `super()` call exist in the constructor * - does the param number passed to `super()` match the expected number * - does the order and the type of the constructor parameters match the expected `parameterClassTypes` * * If only once condition is not satisfied, the method returns `false`. Otherwise, it returns `true`. * * @param source a ts source file * @param inheritedClass a class which customers might have extended * @param parameterClassTypes a list of parameter class types. Must be provided in the order in which they appear in the deprecated constructor. */ function isCandidateForConstructorDeprecation(source, constructorDeprecation) { const nodes = ast_utils_1.getSourceNodes(source); if (!isInheriting(nodes, constructorDeprecation.class)) { return false; } if (!ast_utils_1.isImported(source, constructorDeprecation.class, constructorDeprecation.importPath)) { return false; } if (!checkImports(source, constructorDeprecation.deprecatedParams)) { return false; } const constructorNode = findConstructor(nodes); if (!constructorNode) { return false; } if (!checkConstructorParameters(constructorNode, constructorDeprecation.deprecatedParams)) { return false; } if (!checkSuper(constructorNode, constructorDeprecation.deprecatedParams)) { return false; } return true; } exports.isCandidateForConstructorDeprecation = isCandidateForConstructorDeprecation; function isInheriting(nodes, inheritedClass) { const heritageClauseNodes = nodes.filter((node) => node.kind === typescript_1.default.SyntaxKind.HeritageClause); const heritageNodes = findMultiLevelNodesByTextAndKind(heritageClauseNodes, inheritedClass, typescript_1.default.SyntaxKind.Identifier); return heritageNodes.length !== 0; } exports.isInheriting = isInheriting; function checkImports(source, parameterClassTypes) { for (const classImport of parameterClassTypes) { if (classImport.importPath && !ast_utils_1.isImported(source, classImport.className, classImport.importPath)) { return false; } } return true; } function checkConstructorParameters(constructorNode, parameterClassTypes) { const constructorParameters = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Parameter); const foundClassTypes = []; for (const parameterClassType of parameterClassTypes) { for (const constructorParameter of constructorParameters) { const constructorParameterType = ast_utils_1.findNodes(constructorParameter, typescript_1.default.SyntaxKind.Identifier).filter((node) => node.getText() === parameterClassType.className); if (constructorParameterType.length !== 0) { foundClassTypes.push(parameterClassType); /* the break is needed to cope with multiple parameters of one type, e.g. constructor migrations for constructor( protected cartStore: Store<StateWithMultiCart>, protected store: Store<StateWithConfigurator>, protected configuratorUtilsService: ConfiguratorUtilsService ) {} */ break; } } } return foundClassTypes.length === parameterClassTypes.length; } function isInjected(constructorNode, parameterClassType) { const constructorParameters = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Parameter); for (const constructorParameter of constructorParameters) { const constructorParameterType = ast_utils_1.findNodes(constructorParameter, typescript_1.default.SyntaxKind.Identifier).filter((node) => node.getText() === parameterClassType.className); if (constructorParameterType.length > 0) { return true; } } return false; } function checkSuper(constructorNode, parameterClassTypes) { const constructorBlock = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Block)[0]; const callExpressions = ast_utils_1.findNodes(constructorBlock, typescript_1.default.SyntaxKind.CallExpression); if (callExpressions.length === 0) { return false; } // super has to be the first expression in constructor const firstCallExpression = callExpressions[0]; const superKeyword = ast_utils_1.findNodes(firstCallExpression, typescript_1.default.SyntaxKind.SuperKeyword); if (superKeyword && superKeyword.length === 0) { return false; } const params = ast_utils_1.findNodes(firstCallExpression, typescript_1.default.SyntaxKind.Identifier); if (params.length !== parameterClassTypes.length) { return false; } return true; } function addConstructorParam(source, sourcePath, constructorNode, paramToAdd) { var _a, _b, _c; if (!constructorNode) { throw new schematics_1.SchematicsException(`No constructor found in ${sourcePath}.`); } const changes = []; if (!isInjected(constructorNode, paramToAdd)) { changes.push(injectService({ constructorNode, path: sourcePath, serviceName: paramToAdd.className, modifier: 'no-modifier', propertyType: paramToAdd.literalInference, injectionToken: (_a = paramToAdd.injectionToken) === null || _a === void 0 ? void 0 : _a.token, isArray: (_b = paramToAdd.injectionToken) === null || _b === void 0 ? void 0 : _b.isArray, })); } if (paramToAdd.importPath && !ast_utils_1.isImported(source, paramToAdd.className, paramToAdd.importPath)) { changes.push(ast_utils_1.insertImport(source, sourcePath, paramToAdd.className, paramToAdd.importPath)); } if ((_c = paramToAdd.injectionToken) === null || _c === void 0 ? void 0 : _c.token) { if (!ast_utils_1.isImported(source, constants_1.INJECT_DECORATOR, constants_1.ANGULAR_CORE)) { changes.push(ast_utils_1.insertImport(source, sourcePath, constants_1.INJECT_DECORATOR, constants_1.ANGULAR_CORE)); } /** * This is for the case when an injection token is the same as the import's type. * In this case we don't want to add two imports. * Ex: `@Inject(LaunchRenderStrategy) launchRenderStrategy: LaunchRenderStrategy[]` */ if (paramToAdd.injectionToken.importPath && paramToAdd.injectionToken.token !== paramToAdd.className && !ast_utils_1.isImported(source, paramToAdd.injectionToken.token, paramToAdd.injectionToken.importPath)) { changes.push(ast_utils_1.insertImport(source, sourcePath, paramToAdd.injectionToken.token, paramToAdd.injectionToken.importPath)); } } const paramName = getParamName(source, constructorNode, paramToAdd); changes.push(updateConstructorSuperNode(sourcePath, constructorNode, paramName || paramToAdd.className)); return changes; } exports.addConstructorParam = addConstructorParam; function removeConstructorParam(source, sourcePath, constructorNode, paramToRemove) { if (!constructorNode) { throw new schematics_1.SchematicsException(`No constructor found in ${sourcePath}.`); } const changes = []; if (shouldRemoveImportAndParam(source, paramToRemove)) { const importRemovalChange = removeImport(source, paramToRemove); const injectImportRemovalChange = removeInjectImports(source, constructorNode, paramToRemove); const constructorParamRemovalChanges = removeConstructorParamInternal(sourcePath, constructorNode, paramToRemove); changes.push(importRemovalChange, ...constructorParamRemovalChanges, ...injectImportRemovalChange); } const paramName = getParamName(source, constructorNode, paramToRemove); if (!paramName) { return [new change_1.NoopChange()]; } const superRemoval = removeParamFromSuper(sourcePath, constructorNode, paramName); changes.push(...superRemoval); return changes; } exports.removeConstructorParam = removeConstructorParam; function shouldRemoveDecorator(constructorNode, decoratorIdentifier) { const decoratorParameters = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Decorator).filter((x) => x.getText().includes(decoratorIdentifier)); // if there are 0, or exactly 1 usage of the `decoratorIdentifier` in the whole class, we can safely remove it. return decoratorParameters.length < 2; } exports.shouldRemoveDecorator = shouldRemoveDecorator; function getParamName(source, constructorNode, classType) { const nodes = ast_utils_1.getSourceNodes(source); const constructorParameters = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Parameter); const classDeclarationNode = nodes.find((node) => node.kind === typescript_1.default.SyntaxKind.ClassDeclaration); if (!classDeclarationNode) { return undefined; } for (const constructorParameter of constructorParameters) { if (getClassName(constructorParameter) === classType.className) { const paramVariableNode = constructorParameter .getChildren() .find((node) => node.kind === typescript_1.default.SyntaxKind.Identifier); const paramName = paramVariableNode ? paramVariableNode.getText() : undefined; return paramName; } } return undefined; } function getClassName(constructorParameter) { var _a; const identifierNode = (_a = constructorParameter .getChildren() .find((node) => node.kind === typescript_1.default.SyntaxKind.TypeReference)) === null || _a === void 0 ? void 0 : _a.getChildren().find((node) => node.kind === typescript_1.default.SyntaxKind.Identifier); return identifierNode ? identifierNode.getText() : undefined; } function shouldRemoveImportAndParam(source, importToRemove) { const nodes = ast_utils_1.getSourceNodes(source); const constructorNode = findConstructor(nodes); if (!constructorNode) { return true; } const classDeclarationNode = nodes.find((node) => node.kind === typescript_1.default.SyntaxKind.ClassDeclaration); if (!classDeclarationNode) { return true; } const constructorParameters = getConstructorParameterList(constructorNode); for (const constructorParameter of constructorParameters) { if (constructorParameter.getText().includes(importToRemove.className)) { const paramVariableNode = constructorParameter .getChildren() .find((node) => node.kind === typescript_1.default.SyntaxKind.Identifier); const paramName = paramVariableNode ? paramVariableNode.getText() : ''; const paramUsages = ast_utils_1.findNodes(classDeclarationNode, typescript_1.default.SyntaxKind.Identifier).filter((node) => node.getText() === paramName); // if there are more than two usages (injection and passing to super), then the param is used elsewhere in the class if (paramUsages.length > 2) { return false; } return true; } } return true; } function removeInjectImports(source, constructorNode, paramToRemove) { if (!paramToRemove.injectionToken) { return [new change_1.NoopChange()]; } const importRemovalChange = []; if (shouldRemoveDecorator(constructorNode, constants_1.INJECT_DECORATOR)) importRemovalChange.push(removeImport(source, { className: constants_1.INJECT_DECORATOR, importPath: constants_1.ANGULAR_CORE, })); /** * This is for the case when an injection token is the same as the import's type. * In this case we don't want to have two import removal changes. * Ex: `@Inject(LaunchRenderStrategy) launchRenderStrategy: LaunchRenderStrategy[]` */ if (paramToRemove.injectionToken.importPath && paramToRemove.injectionToken.token !== paramToRemove.className) { importRemovalChange.push(removeImport(source, { className: paramToRemove.injectionToken.token, importPath: paramToRemove.injectionToken.importPath, })); } return importRemovalChange; } exports.removeInjectImports = removeInjectImports; function removeImport(source, importToRemove) { const importDeclarationNode = getImportDeclarationNode(source, importToRemove); if (!importDeclarationNode) { return new change_1.NoopChange(); } let position; let toRemove = importToRemove.className; const importSpecifierNodes = ast_utils_1.findNodes(importDeclarationNode, typescript_1.default.SyntaxKind.ImportSpecifier); if (importSpecifierNodes.length === 1) { // delete the whole import line position = importDeclarationNode.getStart(); toRemove = importDeclarationNode.getText(); } else { // delete only the specified import, and leave the rest const importSpecifier = importSpecifierNodes .map((node, i) => { const importNode = ast_utils_1.findNode(node, typescript_1.default.SyntaxKind.Identifier, importToRemove.className); return { importNode, i, }; }) .filter((result) => result.importNode)[0]; if (!importSpecifier.importNode) { return new change_1.NoopChange(); } // in case the import that needs to be removed is in the middle, we need to remove the ',' that follows the found import if (importSpecifier.i !== importSpecifierNodes.length - 1) { toRemove += ','; } position = importSpecifier.importNode.getStart(); } return new change_1.RemoveChange(source.fileName, position, toRemove); } exports.removeImport = removeImport; function getImportDeclarationNode(source, importToCheck) { if (!importToCheck.importPath) { return undefined; } // collect al the import declarations const importDeclarationNodes = getImportDeclarations(source, importToCheck.importPath); if (importDeclarationNodes.length === 0) { return undefined; } // find the one that contains the specified `importToCheck.className` let importDeclarationNode = importDeclarationNodes[0]; for (const currentImportDeclaration of importDeclarationNodes) { const importIdentifiers = ast_utils_1.findNodes(currentImportDeclaration, typescript_1.default.SyntaxKind.Identifier); const found = importIdentifiers.find((node) => node.getText() === importToCheck.className); if (found) { importDeclarationNode = currentImportDeclaration; break; } } return importDeclarationNode; } function getConstructorParameterList(constructorNode) { const syntaxList = constructorNode .getChildren() .filter((node) => node.kind === typescript_1.default.SyntaxKind.SyntaxList)[0]; return ast_utils_1.findNodes(syntaxList, typescript_1.default.SyntaxKind.Parameter); } function removeConstructorParamInternal(sourcePath, constructorNode, importToRemove) { const constructorParameters = getConstructorParameterList(constructorNode); for (let i = 0; i < constructorParameters.length; i++) { const constructorParameter = constructorParameters[i]; if (constructorParameter.getText().includes(importToRemove.className)) { const changes = []; // if it's not the first parameter that should be removed, we should remove the comma after the previous parameter if (i !== 0) { const previousParameter = constructorParameters[i - 1]; changes.push(new change_1.RemoveChange(sourcePath, previousParameter.end, ',')); // if removing the first param, cleanup the comma after it } else if (i === 0 && constructorParameters.length > 1) { const commas = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.CommaToken); // get the comma that matches the constructor parameter's position const comma = commas[i]; changes.push(new change_1.RemoveChange(sourcePath, comma.getStart(), ',')); } changes.push(new change_1.RemoveChange(sourcePath, constructorParameter.getStart(), constructorParameter.getText())); return changes; } } return []; } function removeParamFromSuper(sourcePath, constructorNode, paramName) { const constructorBlock = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Block)[0]; const callExpressions = ast_utils_1.findNodes(constructorBlock, typescript_1.default.SyntaxKind.CallExpression); if (callExpressions.length === 0) { throw new schematics_1.SchematicsException('No super() call found.'); } const changes = []; // `super()` has to be the first expression in constructor const firstCallExpression = callExpressions[0]; const params = ast_utils_1.findNodes(firstCallExpression, typescript_1.default.SyntaxKind.Identifier); const commas = ast_utils_1.findNodes(firstCallExpression, typescript_1.default.SyntaxKind.CommaToken); for (let i = 0; i < params.length; i++) { const param = params[i]; if (param.getText() === paramName) { if (i !== 0) { const previousCommaPosition = commas[i - 1].getStart(); changes.push(new change_1.RemoveChange(sourcePath, previousCommaPosition, ',')); // if removing the first param, cleanup the comma after it } else if (i === 0 && params.length > 0) { // get the comma that matches the constructor parameter's position const comma = commas[i]; changes.push(new change_1.RemoveChange(sourcePath, comma.getStart(), ',')); } changes.push(new change_1.RemoveChange(sourcePath, param.getStart(), paramName)); break; } } return changes; } function updateConstructorSuperNode(sourcePath, constructorNode, propertyName) { const callBlock = ast_utils_1.findNodes(constructorNode, typescript_1.default.SyntaxKind.Block); propertyName = core_1.strings.camelize(propertyName); if (callBlock.length === 0) { throw new schematics_1.SchematicsException('No constructor body found.'); } const callExpression = ast_utils_1.findNodes(callBlock[0], typescript_1.default.SyntaxKind.CallExpression); // super has to be the first expression in constructor const firstCallExpression = callExpression[0]; const superKeyword = ast_utils_1.findNodes(firstCallExpression, typescript_1.default.SyntaxKind.SuperKeyword); if (superKeyword && superKeyword.length === 0) { throw new schematics_1.SchematicsException('No super() call found.'); } let toInsert = ''; let position; const params = ast_utils_1.findNodes(firstCallExpression, typescript_1.default.SyntaxKind.Identifier); // just an empty super() call, without any params passed to it if (params.length === 0) { position = superKeyword[0].end + 1; } else { const lastParam = params[params.length - 1]; toInsert += ', '; position = lastParam.end; } toInsert += propertyName; return new change_1.InsertChange(sourcePath, position, toInsert); } function injectService(config) { var _a; if (!config.constructorNode) { throw new schematics_1.SchematicsException(`No constructor found in ${config.path}.`); } const constructorParameters = getConstructorParameterList(config.constructorNode); let toInsert = ''; let position = config.constructorNode.getStart() + 'constructor('.length; if (constructorParameters.length > 0) { toInsert += ', '; const lastParam = constructorParameters[constructorParameters.length - 1]; position = lastParam.end; } config.propertyName = config.propertyName ? core_1.strings.camelize(config.propertyName) : core_1.strings.camelize(config.serviceName); config.propertyType = (_a = config.propertyType) !== null && _a !== void 0 ? _a : core_1.strings.classify(config.serviceName); if (config.injectionToken) toInsert += `@Inject(${config.injectionToken}) `; if (config.modifier !== 'no-modifier') toInsert += `${config.modifier} `; toInsert += `${config.propertyName}: ${config.propertyType}`; if (config.isArray) toInsert += '[]'; return new change_1.InsertChange(config.path, position, toInsert); } exports.injectService = injectService; function buildSpartacusComment(comment) { return `// ${constants_1.TODO_SPARTACUS} ${comment}\n`; } exports.buildSpartacusComment = buildSpartacusComment; function insertCommentAboveConfigProperty(sourcePath, source, identifierName, comment) { const identifierNodes = new Set(); ast_utils_1.getSourceNodes(source) .filter((node) => node.kind === typescript_1.default.SyntaxKind.ObjectLiteralExpression) .forEach((objectLiteralNode) => ast_utils_1.findNodes(objectLiteralNode, typescript_1.default.SyntaxKind.Identifier) .filter((node) => node.getText() === identifierName) .forEach((idNode) => identifierNodes.add(idNode))); const changes = []; identifierNodes.forEach((n) => changes.push(new change_1.InsertChange(sourcePath, getLineStartFromTSFile(source, n.getStart()), `${comment}`))); return changes; } exports.insertCommentAboveConfigProperty = insertCommentAboveConfigProperty; function insertCommentAboveIdentifier(sourcePath, source, identifierName, comment, identifierType = typescript_1.default.SyntaxKind.Identifier) { const changes = []; ast_utils_1.getSourceNodes(source).forEach((node) => { if (node.kind !== typescript_1.default.SyntaxKind.ClassDeclaration) { return; } const identifierNodes = ast_utils_1.findNodes(node, identifierType).filter((node) => node.getText() === identifierName); identifierNodes.forEach((n) => changes.push(new change_1.InsertChange(sourcePath, getLineStartFromTSFile(source, n.getStart()), `${comment}`))); }); return changes; } exports.insertCommentAboveIdentifier = insertCommentAboveIdentifier; function getImportDeclarations(source, importPath) { const imports = ast_utils_1.getSourceNodes(source).filter((node) => node.kind === typescript_1.default.SyntaxKind.ImportDeclaration); return imports.filter((imp) => imp.moduleSpecifier .getText() .includes(importPath)); } function filterNamespacedImports(imports) { return imports .filter((imp) => { var _a, _b; return (_b = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings) === null || _b === void 0 ? void 0 : _b.name; }) .filter(Boolean); } function filterNamedImports(imports) { return imports .filter((imp) => { var _a, _b; return (_b = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings) === null || _b === void 0 ? void 0 : _b.elements; }) .filter(Boolean); } function insertCommentAboveImportIdentifier(sourcePath, source, identifierName, importPath, comment) { const imports = getImportDeclarations(source, importPath); const namedImports = filterNamedImports(imports); const namespacedImports = filterNamespacedImports(imports); const namespacedIdentifiers = namespacedImports .map((imp) => { var _a, _b, _c; return (_c = (_b = (_a = imp.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.escapedText; }) .filter(Boolean); const namedImportsWithIdentifierName = namedImports.filter((imp) => ast_utils_1.findNodes(imp, typescript_1.default.SyntaxKind.ImportSpecifier).find((node) => node.name.escapedText === identifierName)); const propertyAccessExpressions = ast_utils_1.getSourceNodes(source).filter((node) => node.kind === typescript_1.default.SyntaxKind.PropertyAccessExpression); const accessPropertiesToIdentifierName = propertyAccessExpressions .filter((member) => { var _a, _b; return namespacedIdentifiers.includes((_b = (_a = member) === null || _a === void 0 ? void 0 : _a.expression) === null || _b === void 0 ? void 0 : _b.escapedText); }) .filter((member) => { var _a, _b; return identifierName === ((_b = (_a = member) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.escapedText); }) .filter(Boolean); const changes = []; namedImportsWithIdentifierName.forEach((n) => changes.push(new change_1.InsertChange(sourcePath, getLineStartFromTSFile(source, n.getStart()), comment))); accessPropertiesToIdentifierName.forEach((n) => changes.push(new change_1.InsertChange(sourcePath, getLineStartFromTSFile(source, n.getStart()), comment))); return changes; } exports.insertCommentAboveImportIdentifier = insertCommentAboveImportIdentifier; function renameIdentifierNode(sourcePath, source, oldName, newName) { const identifierNodes = findLevel1NodesInSourceByTextAndKind(source, oldName, typescript_1.default.SyntaxKind.Identifier); const changes = []; identifierNodes.forEach((n) => changes.push(new change_1.ReplaceChange(sourcePath, n.getStart(), oldName, newName))); return changes; } exports.renameIdentifierNode = renameIdentifierNode; function findLevel1NodesInSourceByTextAndKind(source, text, syntaxKind) { const nodes = ast_utils_1.getSourceNodes(source); return findLevel1NodesByTextAndKind(nodes, text, syntaxKind); } function findLevel1NodesByTextAndKind(nodes, text, syntaxKind) { return nodes .filter((n) => n.kind === syntaxKind) .filter((n) => n.getText() === text); } function findMultiLevelNodesByTextAndKind(nodes, text, syntaxKind) { const result = []; for (const node of nodes) { result.push(...ast_utils_1.findNodes(node, syntaxKind).filter((n) => n.getText() === text)); } return result; } exports.findMultiLevelNodesByTextAndKind = findMultiLevelNodesByTextAndKind; function getLineStartFromTSFile(source, position) { const lac = source.getLineAndCharacterOfPosition(position); return source.getPositionOfLineAndCharacter(lac.line, 0); } // as this is copied from https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/app-shell/index.ts#L211, no need to test Angular's code function getMetadataProperty(metadata, propertyName) { const properties = metadata.properties; const property = properties.filter((prop) => { if (!typescript_1.default.isPropertyAssignment(prop)) { return false; } const name = prop.name; switch (name.kind) { case typescript_1.default.SyntaxKind.Identifier: return name.getText() === propertyName; case typescript_1.default.SyntaxKind.StringLiteral: return name.text === propertyName; } return false; })[0]; return property; } exports.getMetadataProperty = getMetadataProperty; function getLineFromTSFile(host, path, position) { const tsFile = getTsSourceFile(host, path); const lac = tsFile.getLineAndCharacterOfPosition(position); const lineStart = tsFile.getPositionOfLineAndCharacter(lac.line, 0); const nextLineStart = tsFile.getPositionOfLineAndCharacter(lac.line + 1, 0); return [lineStart, nextLineStart - lineStart]; } exports.getLineFromTSFile = getLineFromTSFile; function getServerTsPath(host) { var _a, _b, _c; const projectName = workspace_utils_1.getDefaultProjectNameFromWorkspace(host); const angularJson = workspace_utils_1.getAngularJsonFile(host); return (_c = (_b = (_a = angularJson.projects[projectName].architect) === null || _a === void 0 ? void 0 : _a.server) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.main; } exports.getServerTsPath = getServerTsPath; //# sourceMappingURL=file-utils.js.map