@taiga-ui/cdk
Version:
Base library for creating Angular components and applications using Taiga UI principles regarding of actual visual appearance
156 lines • 7.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrateTokens = migrateTokens;
const ng_morph_1 = require("ng-morph");
const constants_1 = require("../../../../constants");
const colored_log_1 = require("../../../../utils/colored-log");
const get_named_import_references_1 = require("../../../../utils/get-named-import-references");
const import_manipulations_1 = require("../../../../utils/import-manipulations");
const insert_todo_1 = require("../../../../utils/insert-todo");
const project_root_1 = require("../../../../utils/project-root");
const TOKEN_FUNCTIONS = ['tuiCreateToken', 'tuiCreateTokenFromFactory'];
const ANGULAR_CORE = '@angular/core';
const INJECTION_TOKEN = 'InjectionToken';
/**
* Migrates Taiga UI token functions to Angular's InjectionToken
*
* This schematic:
* 1. Finds all references to `tuiCreateToken` and `tuiCreateTokenFromFactory`
* 2. Replaces them with equivalent `InjectionToken` implementations
* 3. Handles proper import management for `InjectionToken`
* 4. Preserves type parameters and factory functions
* 5. Adds descriptive error handling and logging
*/
function migrateTokens(tree, options) {
if (!options['skip-logs']) {
(0, colored_log_1.infoLog)('Starting token migration to InjectionToken...');
}
const project = (0, ng_morph_1.createProject)(tree, (0, project_root_1.projectRoot)(), constants_1.ALL_TS_FILES);
(0, ng_morph_1.setActiveProject)(project);
const allFiles = (0, ng_morph_1.getSourceFiles)();
if (!options['skip-logs']) {
(0, colored_log_1.infoLog)(`Found ${allFiles.length} source files`);
}
TOKEN_FUNCTIONS.forEach((tokenFunction) => {
const references = (0, get_named_import_references_1.getNamedImportReferences)(tokenFunction, '@taiga-ui/cdk');
if (!options['skip-logs']) {
(0, colored_log_1.infoLog)(`Found ${references.length} references for ${tokenFunction}`);
}
references.forEach((ref) => {
if (ref.wasForgotten()) {
return;
}
try {
const parent = ref.getParent();
const sourceFile = ref.getSourceFile();
if (ng_morph_1.Node.isImportSpecifier(parent)) {
(0, import_manipulations_1.removeImport)(parent);
return;
}
if (ng_morph_1.Node.isCallExpression(parent)) {
const declaration = parent.getFirstAncestorByKind(ng_morph_1.SyntaxKind.VariableDeclaration);
if (!declaration) {
if (!ref.wasForgotten()) {
(0, insert_todo_1.insertTodo)(ref, 'Could not find variable declaration');
}
return;
}
const constName = declaration.getName();
if (!constName) {
if (!ref.wasForgotten()) {
(0, insert_todo_1.insertTodo)(ref, 'Variable declaration has no name');
}
return;
}
const newExpression = createInjectionTokenExpression(parent, constName, tokenFunction);
parent.replaceWithText(newExpression);
// handle InjectionToken imports
let hasValueImport = false;
const coreImports = [];
sourceFile.getImportDeclarations().forEach((decl) => {
if (decl.getModuleSpecifierValue() !== ANGULAR_CORE) {
return;
}
coreImports.push(decl);
const namedImports = decl.getNamedImports();
const tokenImport = namedImports.find((i) => i.getName() === INJECTION_TOKEN);
if (!tokenImport) {
return;
}
// remove InjectionToken from type-only imports
if (tokenImport.isTypeOnly() || decl.isTypeOnly()) {
tokenImport.remove();
// remove the import declaration if it becomes empty
if (decl.getNamedImports().length === 0) {
decl.remove();
}
}
else {
hasValueImport = true;
}
});
// add InjectionToken as regular import if needed
if (!hasValueImport) {
sourceFile.addImportDeclaration({
moduleSpecifier: ANGULAR_CORE,
namedImports: [INJECTION_TOKEN],
isTypeOnly: false,
});
}
}
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (!options['skip-logs']) {
(0, colored_log_1.infoLog)(`Error migrating token: ${message}`);
}
if (!ref.wasForgotten()) {
(0, insert_todo_1.insertTodo)(ref, `Migration failed: ${message}`);
}
}
});
});
(0, ng_morph_1.saveActiveProject)();
if (!options['skip-logs']) {
(0, colored_log_1.titleLog)('Token migration completed!');
}
}
/**
* Creates InjectionToken expression to replace token functions
*
* This function:
* - Preserves type parameters (<T> syntax)
* - Converts token values to factory functions when needed
* - Adds ngDevMode checks for token descriptions
* - Handles different cases for tuiCreateToken vs tuiCreateTokenFromFactory
*
* Example conversions:
* tuiCreateToken('default') → new InjectionToken(..., { factory: () => 'default' })
* tuiCreateTokenFromFactory(() => value) → new InjectionToken(..., { factory: () => value })
*/
function createInjectionTokenExpression(callExpression, constName, tokenFunction) {
const typeArgs = callExpression.getTypeArguments();
const typeArgsText = typeArgs.length
? `<${typeArgs.map((t) => t.getText()).join(', ')}>`
: '';
const args = callExpression.getArguments();
const isFactory = tokenFunction === 'tuiCreateTokenFromFactory';
const tokenDescription = `ngDevMode ? '${constName}' : ''`;
if (args.length > 0 && args[0]) {
const argText = args[0].getText();
const isObjectOrArray = argText.startsWith('{') || argText.startsWith('[');
if (isFactory) {
return `new ${INJECTION_TOKEN}${typeArgsText}(${tokenDescription}, {
factory: ${argText}
})`;
}
const factoryContent = isObjectOrArray
? `() => (${argText})`
: `() => ${argText}`;
return `new ${INJECTION_TOKEN}${typeArgsText}(${tokenDescription}, {
factory: ${factoryContent}
})`;
}
return `new ${INJECTION_TOKEN}${typeArgsText}(${tokenDescription})`;
}
//# sourceMappingURL=migrate-tokens.js.map