UNPKG

igniteui-angular

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

772 lines (771 loc) • 35.8 kB
"use strict"; var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BindingType = exports.UpdateChanges = exports.InputPropertyType = void 0; const fs = require("fs"); const path = require("path"); const ts = require("typescript"); const tss = require("typescript/lib/tsserverlibrary"); const schema_1 = require("./schema"); const tsUtils_1 = require("./tsUtils"); const util_1 = require("./util"); const project_service_container_1 = require("./project-service-container"); const TSCONFIG_PATH = 'tsconfig.json'; var InputPropertyType; (function (InputPropertyType) { InputPropertyType["EVAL"] = "eval"; InputPropertyType["STRING"] = "string"; })(InputPropertyType || (exports.InputPropertyType = InputPropertyType = {})); /* eslint-disable arrow-parens */ class UpdateChanges { get shouldInvokeLS() { return this._shouldInvokeLS; } set shouldInvokeLS(val) { if (val === undefined || val === null) { // call LS by default this.shouldInvokeLS = true; return; } this._shouldInvokeLS = val; } get projectService() { const projectService = project_service_container_1.serviceContainer.projectService; if (!project_service_container_1.serviceContainer.configured) { this.configureForAngularLS(projectService); project_service_container_1.serviceContainer.configured = true; } return projectService; } get serverHost() { return project_service_container_1.serviceContainer.serverHost; } get templateFiles() { if (!this._templateFiles.length) { // https://github.com/angular/devkit/blob/master/packages/angular_devkit/schematics/src/tree/filesystem.ts this.sourceDirsVisitor((fulPath, entry) => { if (fulPath.endsWith('component.html')) { this._templateFiles.push(entry.path); } }); } return this._templateFiles; } get tsFiles() { if (!this._tsFiles.length) { this.sourceDirsVisitor((fulPath, entry) => { if (fulPath.endsWith('.ts')) { this._tsFiles.push(entry.path); } }); } return this._tsFiles; } /** Sass (both .scss and .sass) files in the project being updated. */ get sassFiles() { if (!this._sassFiles.length) { // files can be outside the app prefix, so start from sourceRoot // also ignore schematics `styleext` as Sass can be used regardless const sourceDirs = (0, util_1.getProjects)(this.workspace).map(x => x.sourceRoot).filter(x => x); this.sourceDirsVisitor((fulPath, entry) => { if (fulPath.endsWith('.scss') || fulPath.endsWith('.sass')) { this._sassFiles.push(entry.path); } }, sourceDirs); } return this._sassFiles; } get service() { if (!this._service) { this._service = (0, tsUtils_1.getLanguageService)(this.tsFiles, this.host); } return this._service; } get packageManager() { if (!this._packageManager) { this._packageManager = (0, util_1.getPackageManager)(this.host); } return this._packageManager; } /** * Create a new base schematic to apply changes * * @param rootPath Root folder for the schematic to read configs, pass __dirname */ constructor(rootPath, host, context) { this.rootPath = rootPath; this.host = host; this.context = context; this.tsconfigPath = TSCONFIG_PATH; this._shouldInvokeLS = true; this.conditionFunctions = new Map(); this.valueTransforms = new Map(); this._templateFiles = []; this._initialTsConfig = ''; this._tsFiles = []; this._sassFiles = []; this.workspace = (0, util_1.getWorkspace)(host); this.sourcePaths = (0, util_1.getProjectPaths)(this.workspace); this.selectorChanges = this.loadConfig('selectors.json'); this.classChanges = this.loadConfig('classes.json'); this.outputChanges = this.loadConfig('outputs.json'); this.inputChanges = this.loadConfig('inputs.json'); this.themeChanges = this.loadConfig('theme-changes.json'); this.importsChanges = this.loadConfig('imports.json'); this.membersChanges = this.loadConfig('members.json'); // update LS server host with the schematics tree: this.serverHost.host = this.host; } /** Apply configured changes to the Host Tree */ applyChanges() { const shouldInstallPkg = this.membersChanges && this.membersChanges.changes.length && !(0, util_1.canResolvePackage)(tsUtils_1.NG_LANG_SERVICE_PACKAGE_NAME); if (shouldInstallPkg) { this.context.logger.info(`Installing temporary migration dependencies via ${this.packageManager}.`); // try and get an appropriate version of the package to install let targetVersion = (0, util_1.getPackageVersion)(tsUtils_1.NG_CORE_PACKAGE_NAME) || 'latest'; if (targetVersion.startsWith('11')) { // TODO: Temporary restrict 11 LS version, till update for new module loading targetVersion = '11.0.0'; } (0, util_1.tryInstallPackage)(this.context, this.packageManager, `${tsUtils_1.NG_LANG_SERVICE_PACKAGE_NAME}@${targetVersion}`); } this.updateTemplateFiles(); this.updateTsFiles(); if (this.shouldInvokeLS) { this.updateMembers(); } /** Sass files */ if (this.themeChanges && this.themeChanges.changes.length) { for (const entryPath of this.sassFiles) { this.updateThemeProps(entryPath); this.updateSassVariables(entryPath); this.updateSassFunctionsAndMixins(entryPath); } } if (shouldInstallPkg) { this.context.logger.info(`Cleaning up temporary migration dependencies.`); (0, util_1.tryUninstallPackage)(this.context, this.packageManager, tsUtils_1.NG_LANG_SERVICE_PACKAGE_NAME); } // if tsconfig.json was patched, restore it if (this._initialTsConfig !== '') { this.host.overwrite(this.tsconfigPath, this._initialTsConfig); } } /** Add condition function. */ addCondition(conditionName, callback) { this.conditionFunctions.set(conditionName, callback); } addValueTransform(functionName, callback) { this.valueTransforms.set(functionName, callback); } /** Path must be absolute. If calling externally, use this.getAbsolutePath */ getDefaultLanguageService(entryPath) { const project = this.getDefaultProjectForFile(entryPath); return project === null || project === void 0 ? void 0 : project.getLanguageService(); } updateSelectors(entryPath) { let fileContent = this.host.read(entryPath).toString(); let overwrite = false; for (const change of this.selectorChanges.changes) { let searchPttrn = change.type === 'component' ? '<' : ''; searchPttrn += change.selector; if (fileContent.indexOf(searchPttrn) !== -1) { fileContent = this.applySelectorChange(fileContent, change); overwrite = true; } } if (overwrite) { this.host.overwrite(entryPath, fileContent); } } applySelectorChange(fileContent, change) { let regSource; let replace; switch (change.type) { case 'component': if (change.remove) { regSource = String.raw `\<${change.selector}[\s\S]*?\<\/${change.selector}\>`; replace = ''; } else { regSource = String.raw `\<(\/?)${change.selector}(?=[\s\>])`; replace = `<$1${change.replaceWith}`; } break; case 'directive': if (change.remove) { // Group match (\2) as variable as it looks like octal escape (error in strict) regSource = String.raw `\s*?\[?${change.selector}\]?(=(["']).*?${'\\2'}(?=\s|\>))?`; replace = ''; } else { regSource = change.selector; replace = change.replaceWith; } break; default: break; } fileContent = fileContent.replace(new RegExp(regSource, 'g'), replace); return fileContent; } updateClasses(entryPath) { let fileContent = this.host.read(entryPath).toString(); const alreadyReplaced = new Set(); for (const change of this.classChanges.changes) { if (fileContent.indexOf(change.name) !== -1) { const positions = (0, tsUtils_1.getRenamePositions)(entryPath, change.name, this.service); // loop backwards to preserve positions for (let i = positions.length; i--;) { const pos = positions[i]; // V.S. 18th May 2021: If several classes are renamed w/ the same import, erase them // TODO: Refactor to make use of TSLS API instead of string replace if (i === 0 && alreadyReplaced.has(change.replaceWith)) { // only match the first trailing white space, right after the replace position const trailingCommaWhiteSpace = new RegExp(/,([\s]*)(?=(\s}))/); let afterReplace = fileContent.slice(pos.end); const beforeReplace = fileContent.slice(0, pos.start); const leadingComma = afterReplace[0] === ',' ? 1 : 0; // recalculate if needed afterReplace = !leadingComma ? afterReplace : fileContent.slice(pos.end + leadingComma); const doubleSpaceReplace = beforeReplace[beforeReplace.length - 1].match(/\s/) !== null && afterReplace[0].match(/\s/) !== null ? 1 : 0; fileContent = (fileContent.slice(0, pos.start - doubleSpaceReplace) + '' + afterReplace).replace(trailingCommaWhiteSpace, ''); } else { fileContent = fileContent.slice(0, pos.start) + change.replaceWith + fileContent.slice(pos.end); } } if (positions.length) { // using a set should be a lot quicker that getting position for renames of replace alreadyReplaced.add(change.replaceWith); this.host.overwrite(entryPath, fileContent); } } } } updateBindings(entryPath, bindChanges, type = BindingType.Output) { let fileContent = this.host.read(entryPath).toString(); let overwrite = false; for (const change of bindChanges.changes) { if (fileContent.indexOf(change.owner.selector) === -1 || fileContent.indexOf(change.name) === -1) { continue; } let base; let replace; let searchPattern; if (type === BindingType.Output) { base = String.raw(templateObject_1 || (templateObject_1 = __makeTemplateObject(["(", void 0], ["\\(", "\\)=([\"'])(.*?)\\1"])), change.name); replace = `(${change.replaceWith})=$1$2$1`; } else { // Match both bound - [name] - and regular - name base = String.raw(templateObject_2 || (templateObject_2 = __makeTemplateObject(["(s[?)", void 0], ["(\\s\\[?)", "(\\s*\\]?=)([\"'])(.*?)\\3"])), change.name); replace = String.raw `$1${change.replaceWith}$2$3$4$3`; } let reg = new RegExp(base, 'g'); if (change.remove || change.moveBetweenElementTags) { // Group match (\1) as variable as it looks like octal escape (error in strict) reg = new RegExp(String.raw `\s*${base}(?=\s|\>)`, 'g'); replace = ''; } switch (change.owner.type) { case 'component': searchPattern = String.raw `\<${change.owner.selector}(?=[\s\>])[^\>]*\>`; break; case 'directive': searchPattern = String.raw `\<[^\>]*[\s\[]${change.owner.selector}[^\>]*\>`; break; } const matches = fileContent.match(new RegExp(searchPattern, 'g')); if (!matches) { continue; } for (const match of matches) { let replaceStatement = replace; if (!this.areConditionsFulfilled(match, change.conditions, entryPath)) { continue; } if (change.moveBetweenElementTags) { const moveMatch = match.match(reg); fileContent = this.copyPropertyValueBetweenElementTags(fileContent, match, moveMatch); } if (change.valueTransform) { const regExpMatch = match.match(new RegExp(base)); const bindingType = regExpMatch && regExpMatch[1].endsWith('[') ? InputPropertyType.EVAL : InputPropertyType.STRING; if (regExpMatch) { const value = regExpMatch[4]; const transform = this.valueTransforms.get(change.valueTransform); const args = { value, bindingType }; transform(args); if (args.bindingType !== bindingType) { replaceStatement = args.bindingType === InputPropertyType.EVAL ? replaceStatement.replace(`$1`, `$1[`).replace(`$2`, `]$2`) : replaceStatement.replace(`$1`, regExpMatch[1].replace('[', '')).replace('$2', regExpMatch[2].replace(']', '')); } replaceStatement = replaceStatement.replace('$4', args.value); } } fileContent = fileContent.replace(match, match.replace(reg, replaceStatement)); } overwrite = true; } if (overwrite) { this.host.overwrite(entryPath, fileContent); } } updateThemeProps(entryPath) { var _a; let fileContent = this.host.read(entryPath).toString(); let overwrite = false; for (const change of this.themeChanges.changes) { if (change.type !== schema_1.ThemeType.Property) { continue; } if (fileContent.indexOf(change.owner) !== -1) { /** owner-func:( * ); */ const searchPattern = String.raw `${change.owner}\([\s\S]+?\);`; const matches = fileContent.match(new RegExp(searchPattern, 'g')); if (!matches) { continue; } for (const match of matches) { if (match.indexOf(change.name) !== -1) { const name = change.name.replace('$', '\\$'); const replaceWith = (_a = change.replaceWith) === null || _a === void 0 ? void 0 : _a.replace('$', '\\$'); const reg = new RegExp(String.raw `^\s*${name}:`); const existing = new RegExp(String.raw `${replaceWith}:`); const opening = `${change.owner}(`; const closing = /\s*\);$/.exec(match).pop(); const body = match.substr(opening.length, match.length - opening.length - closing.length); let params = this.splitFunctionProps(body); params = params.reduce((arr, param) => { if (reg.test(param)) { const duplicate = !!replaceWith && arr.some(p => existing.test(p)); if (!change.remove && !duplicate) { arr.push(param.replace(change.name, change.replaceWith)); } } else { arr.push(param); } return arr; }, []); fileContent = fileContent.replace(match, opening + params.join(',') + closing); overwrite = true; } } } } if (overwrite) { this.host.overwrite(entryPath, fileContent); } } isNamedArgument(fileContent, i, occurrences, change) { const openingBrackets = []; const closingBrackets = []; if (fileContent[(occurrences[i] + change.name.length)] !== ':' || (fileContent[(occurrences[i] + change.name.length)] === ' ' && fileContent[(occurrences[i] + change.name.length) + 1] === ':')) { return false; } for (let j = occurrences[i]; j >= 0; j--) { if (fileContent[j] === ')') { closingBrackets.push(fileContent[j]); } else if (fileContent[j] === '(') { openingBrackets.push(fileContent[j]); } } return openingBrackets.length !== closingBrackets.length; } updateSassVariables(entryPath) { let fileContent = this.host.read(entryPath).toString(); let overwrite = false; const allowedStartCharacters = new RegExp(/(:|,)\s?/, 'g'); // eslint-disable-next-line no-control-regex const allowedEndCharacters = new RegExp('[;),: \r\n]', 'g'); for (const change of this.themeChanges.changes) { if (change.type !== schema_1.ThemeType.Variable) { continue; } if (!('owner' in change)) { const occurrences = (0, tsUtils_1.findMatches)(fileContent, change.name); for (let i = occurrences.length - 1; i >= 0; i--) { const allowedStartEnd = fileContent[occurrences[i] - 1].match(allowedStartCharacters) || fileContent[(occurrences[i] + change.name.length)].match(allowedEndCharacters); if (allowedStartEnd && !this.isNamedArgument(fileContent, i, occurrences, change)) { fileContent = (0, util_1.replaceMatch)(fileContent, change.name, change.replaceWith, occurrences[i]); overwrite = true; } } } } if (overwrite) { this.host.overwrite(entryPath, fileContent); } } updateSassFunctionsAndMixins(entryPath) { const aliases = this.getAliases(entryPath); let fileContent = this.host.read(entryPath).toString(); let overwrite = false; for (const change of this.themeChanges.changes) { if (change.type !== schema_1.ThemeType.Function && change.type !== schema_1.ThemeType.Mixin) { continue; } let occurrences = []; if (aliases.length > 0 && !aliases.includes('*')) { aliases.forEach(a => occurrences = occurrences.concat((0, tsUtils_1.findMatches)(fileContent, a + '.' + change.name))); if (occurrences.length > 0) { ({ overwrite, fileContent } = this.tryReplaceScssFunctionWithAlias(occurrences, aliases, fileContent, change, overwrite)); continue; } } occurrences = (0, tsUtils_1.findMatches)(fileContent, change.name); if (occurrences.length > 0) { ({ overwrite, fileContent } = this.tryReplaceScssFunction(occurrences, fileContent, change, overwrite)); } } if (overwrite) { this.host.overwrite(entryPath, fileContent); } } getAliases(entryPath) { const fileContent = this.host.read(entryPath).toString(); // B.P. 18/05/22 #11577 - Use RegEx to distinguish themed imports. const matchers = [ /@use(\s+)('|")igniteui-angular\/theming\2\1as\1(\w+)/g, /@use(\s+)('|")igniteui-angular\/theme\2\1as\1(\w+)/g, /@use(\s+)('|")igniteui-angular\/lib\/core\/styles\/themes\/index\2\1as\1(\w+)/g ]; const aliases = []; matchers.forEach(m => { const match = m.exec(fileContent); if (match) { aliases.push(match[3]); // access the captured alias } }); return aliases; } updateImports(entryPath) { let fileContent = this.host.read(entryPath).toString(); let overwrite = false; for (const change of this.importsChanges.changes) { if (fileContent.indexOf(change.name) === -1) { continue; } const replace = (0, util_1.escapeRegExp)(change.replaceWith); const base = (0, util_1.escapeRegExp)(change.name); const reg = new RegExp(base, 'g'); fileContent = fileContent.replace(reg, replace); overwrite = true; } if (overwrite) { this.host.overwrite(entryPath, fileContent); } } updateClassMembers(entryPath, memberChanges) { let content = this.host.read(entryPath).toString(); const absPath = tss.server.toNormalizedPath(path.join(process.cwd(), entryPath)); // use the absolute path for ALL LS operations // do not overwrite the entryPath, as Tree operations require relative paths const changes = new Set(); let langServ; for (const change of memberChanges.changes) { if (!content.includes(change.member)) { continue; } langServ = langServ || this.getDefaultLanguageService(absPath); if (!langServ) { return; } let matches; if (entryPath.endsWith('.ts')) { const source = langServ.getProgram().getSourceFile(absPath); matches = (0, tsUtils_1.getIdentifierPositions)(source, change.member).map(x => x.start); } else { matches = (0, tsUtils_1.findMatches)(content, `.${change.member}`).map(pos => pos + 1); } for (const matchPosition of matches) { if ((0, tsUtils_1.isMemberIgniteUI)(change, langServ, absPath, matchPosition)) { changes.add({ change, position: matchPosition }); } } } const changesArr = Array.from(changes).sort((c, c1) => c.position - c1.position).reverse(); for (const fileChange of changesArr) { content = (0, util_1.replaceMatch)(content, fileChange.change.member, fileChange.change.replaceWith, fileChange.position); } if (changes.size) { this.host.overwrite(entryPath, content); } } // TODO: combine both functions tryReplaceScssFunctionWithAlias(occurrences, aliases, fileContent, change, overwrite) { for (const alias of aliases) { const aliasLength = alias.length + 1; // + 1 because of the dot - alias.member for (let i = occurrences.length - 1; i >= 0; i--) { const isOpenParenthesis = fileContent[occurrences[i] + aliasLength + change.name.length] === '('; if (isOpenParenthesis) { fileContent = (0, util_1.replaceMatch)(fileContent, change.name, change.replaceWith, occurrences[i] + aliasLength); overwrite = true; } } } return { overwrite, fileContent }; } tryReplaceScssFunction(occurrences, fileContent, change, overwrite) { for (let i = occurrences.length - 1; i >= 0; i--) { const isOpenParenthesis = fileContent[occurrences[i] + change.name.length] === '('; if (isOpenParenthesis) { fileContent = (0, util_1.replaceMatch)(fileContent, change.name, change.replaceWith, occurrences[i]); overwrite = true; } } return { overwrite, fileContent }; } patchTsConfig() { var _a, _b, _c, _d; this.ensureTsConfigPath(); if (this.serverHost.fileExists(this.tsconfigPath)) { let originalContent = ''; try { originalContent = this.serverHost.readFile(this.tsconfigPath); } catch (_e) { (_a = this.context) === null || _a === void 0 ? void 0 : _a.logger.warn(`Could not read ${this.tsconfigPath}. Some Angular Ivy features might be unavailable during migrations.`); return; } let content; // use ts parser as it handles jsonc-style files w/ comments const result = ts.parseConfigFileTextToJson(this.tsconfigPath, originalContent); if (!result.error) { content = result.config; } else { (_b = this.context) === null || _b === void 0 ? void 0 : _b.logger.warn(`Could not parse ${this.tsconfigPath}. Angular Ivy language service might be unavailable during migrations.`); (_c = this.context) === null || _c === void 0 ? void 0 : _c.logger.warn(`Error:\n${result.error}`); return; } if (!content.angularCompilerOptions) { content.angularCompilerOptions = {}; } if (!content.angularCompilerOptions.strictTemplates) { (_d = this.context) === null || _d === void 0 ? void 0 : _d.logger.info(`Adding 'angularCompilerOptions.strictTemplates' to ${this.tsconfigPath} for migration run.`); content.angularCompilerOptions.strictTemplates = true; this.host.overwrite(this.tsconfigPath, JSON.stringify(content)); // store initial state and restore it once migrations are finished this._initialTsConfig = originalContent; } } } ensureTsConfigPath() { var _a, _b; if (this.host.exists(this.tsconfigPath)) { return; } // attempt to find a main tsconfig from workspace: const wsProject = Object.values(this.workspace.projects)[0]; // technically could be per-project, but assuming there's at least one main tsconfig for IDE support const projectConfig = (_b = (_a = wsProject.architect) === null || _a === void 0 ? void 0 : _a.build) === null || _b === void 0 ? void 0 : _b.options['tsConfig']; if (!projectConfig || !this.host.exists(projectConfig)) { return; } if (path.posix.basename(projectConfig) === TSCONFIG_PATH) { // not project specific extended tsconfig, use directly this.tsconfigPath = projectConfig; return; } // look for base config through extends property const result = ts.parseConfigFileTextToJson(projectConfig, this.serverHost.readFile(projectConfig)); if (!result.error && result.config.extends) { this.tsconfigPath = path.posix.join(path.posix.dirname(projectConfig), result.config.extends); } } loadConfig(configJson) { const filePath = path.join(this.rootPath, 'changes', configJson); if (fs.existsSync(filePath)) { return JSON.parse(fs.readFileSync(filePath, 'utf-8')); } } areConditionsFulfilled(match, conditions, entryPath) { if (conditions) { for (const condition of conditions) { if (this.conditionFunctions && this.conditionFunctions.has(condition)) { const callback = this.conditionFunctions.get(condition); if (callback && !callback(match, entryPath)) { return false; } } } } return true; } copyPropertyValueBetweenElementTags(fileContent, ownerMatch, propertyMatchArray) { if (ownerMatch && propertyMatchArray && propertyMatchArray.length > 0) { const propMatch = propertyMatchArray[0].trim(); const propValueMatch = propMatch.match(new RegExp(`=(["'])(.+?)${'\\1'}`)); if (propValueMatch && propValueMatch.length > 0) { const propValue = propValueMatch[propValueMatch.length - 1]; if (propMatch.startsWith('[')) { return fileContent.replace(ownerMatch, ownerMatch + `{{${propValue}}}`); } else { return fileContent.replace(ownerMatch, ownerMatch + propValue); } } } return fileContent; } sourceDirsVisitor(visitor, dirs = this.sourcePaths) { for (const sourcePath of dirs) { const srcDir = this.host.getDir(sourcePath); srcDir.visit(visitor); } } /** * Safe split by `','`, considering possible inner function calls. E.g.: * ``` * prop: inner-func(), * prop2: inner2(inner-param: 3, inner-param: inner-func(..)) * ``` */ splitFunctionProps(body) { const parts = []; let lastIndex = 0; let level = 0; for (let i = 0; i < body.length; i++) { const char = body[i]; switch (char) { case '(': level++; break; case ')': level--; break; case ',': if (!level) { parts.push(body.substring(lastIndex, i)); lastIndex = i + 1; } break; default: break; } } parts.push(body.substring(lastIndex)); return parts; } updateTemplateFiles() { if (this.selectorChanges && this.selectorChanges.changes.length) { for (const entryPath of this.templateFiles) { this.updateSelectors(entryPath); } } if (this.outputChanges && this.outputChanges.changes.length) { // name change of output for (const entryPath of this.templateFiles) { this.updateBindings(entryPath, this.outputChanges); } } if (this.inputChanges && this.inputChanges.changes.length) { // name change of input for (const entryPath of this.templateFiles) { this.updateBindings(entryPath, this.inputChanges, BindingType.Input); } } } updateTsFiles() { if (this.classChanges && this.classChanges.changes.length) { // change class name for (const entryPath of this.tsFiles) { this.updateClasses(entryPath); } } if (this.importsChanges && this.importsChanges.changes.length) { // TODO: move logic to 7.0.2 migration for (const entryPath of this.tsFiles) { this.updateImports(entryPath); } } } updateMembers() { if (this.membersChanges && this.membersChanges.changes.length) { const dirs = [...this.templateFiles, ...this.tsFiles]; for (const entryPath of dirs) { this.updateClassMembers(entryPath, this.membersChanges); } } } getDefaultProjectForFile(entryPath) { var _a; const scriptInfo = (_a = this.projectService) === null || _a === void 0 ? void 0 : _a.getOrCreateScriptInfoForNormalizedPath(tss.server.asNormalizedPath(entryPath), false); if (!scriptInfo) { return null; } this.projectService.openClientFile(scriptInfo.fileName); const project = this.projectService.findProject(scriptInfo.containingProjects[0].getProjectName()); project.addMissingFileRoot(scriptInfo.fileName); return project; } /** * Force Angular service to compile project on initial load w/ configured project * otherwise if the first compilation occurs on an HTML file the project won't have proper refs * and no actual angular metadata will be resolved for the rest of the migration */ configureForAngularLS(projectService) { // TODO: this pattern/issue might be obsolete const mainRelPath = this.getWorkspaceProjectEntryPath(); if (!mainRelPath) { return; } // patch TSConfig so it includes angularOptions.strictTemplates // ivy ls requires this in order to function properly on templates this.patchTsConfig(); const mainAbsPath = path.resolve(projectService.currentDirectory, mainRelPath); const scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(tss.server.toNormalizedPath(mainAbsPath), false); projectService.openClientFile(scriptInfo.fileName); try { const project = projectService.findProject(scriptInfo.containingProjects[0].getProjectName()); project.getLanguageService().getSemanticDiagnostics(mainAbsPath); } catch (err) { this.context.logger.warn("An error occurred during TypeScript project service setup. Some migrations relying on language services might not be applied."); } } getWorkspaceProjectEntryPath() { var _a, _b; const projectKeys = Object.keys(this.workspace.projects); if (!projectKeys.length) { this.context.logger.info(`Could not resolve project from directory ${this.serverHost.getCurrentDirectory()}. Some migrations may not be applied.`); return null; } // grab the first possible main path: for (const key of projectKeys) { const wsProject = this.workspace.projects[key]; // intentionally compare against string values of the enum to avoid hard import if (wsProject.projectType == "application" && ((_b = (_a = wsProject.architect) === null || _a === void 0 ? void 0 : _a.build) === null || _b === void 0 ? void 0 : _b.options)) { return wsProject.architect.build.options['browser'] || wsProject.architect.build.options['main']; } else if (wsProject.projectType == "library") { // TODO: attempt to resolve from project ng-package.json or tsConfig } } return null; } } exports.UpdateChanges = UpdateChanges; var BindingType; (function (BindingType) { BindingType[BindingType["Output"] = 0] = "Output"; BindingType[BindingType["Input"] = 1] = "Input"; })(BindingType || (exports.BindingType = BindingType = {})); var templateObject_1, templateObject_2;