UNPKG

@nstudio/schematics

Version:

Cross-platform (xplat) tools for Nx workspaces.

529 lines 25.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const schematics_1 = require("@angular-devkit/schematics"); const ast_1 = require("./ast"); const errors_1 = require("./errors"); const general_1 = require("./general"); const ts = require("typescript"); const format_files_1 = require("./format-files"); function generate(type, options) { if (!options.name) { throw new Error(errors_1.generateOptionError(type)); } let featureName = getFeatureName(options); let platforms = []; if (options.projects) { // building in projects const projects = general_1.sanitizeCommaDelimitedArg(options.projects); for (const name of projects) { const nameParts = name.split("-"); const platPrefix = nameParts[0]; const platSuffix = nameParts[nameParts.length - 1]; if (general_1.supportedPlatforms.includes(platPrefix) && !platforms.includes(platPrefix)) { // if project name is prefixed with supported platform and not already added platforms.push(platPrefix); } else if (general_1.supportedPlatforms.includes(platSuffix) && !platforms.includes(platSuffix)) { // if project name is suffixed with supported platform and not already added platforms.push(platSuffix); } } } else if (options.platforms) { // building in shared code only platforms = general_1.sanitizeCommaDelimitedArg(options.platforms); } const targetPlatforms = {}; for (const t of platforms) { if (general_1.supportedPlatforms.includes(t)) { targetPlatforms[t] = true; } else { throw new Error(errors_1.unsupportedPlatformError(t)); } } const projectChains = []; if (options.projects) { for (const projectName of options.projects.split(",")) { const platPrefix = projectName.split("-")[0]; let srcDir = platPrefix !== "nativescript" ? "src/" : ""; const prefixPath = `apps/${projectName}/${srcDir}app`; let featurePath; if (shouldTargetCoreBarrel(type, featureName)) { featureName = "core"; featurePath = `${prefixPath}/${featureName}`; } else { featurePath = `${prefixPath}/features/${featureName}`; } const featureModulePath = `${featurePath}/${featureName}.module.ts`; let barrelIndex; if (type === "state") { barrelIndex = `${featurePath}/index.ts`; } else { barrelIndex = `${featurePath}/${type}s/index.ts`; } // console.log('will adjustProject:', projectName); projectChains.push((tree, context) => { if (!tree.exists(featureModulePath)) { throw new Error(errors_1.needFeatureModuleError(featureModulePath, featureName, projectName, true)); } return addToFeature(type, options, prefixPath, tree)(tree, context); }); if (type === "state") { // ngrx handling projectChains.push((tree, context) => { return adjustBarrelIndexForType(type, options, barrelIndex)(tree, context); }); projectChains.push((tree, context) => { return addToFeature(type, options, prefixPath, tree, "_index")(tree, context); }); projectChains.push((tree, context) => { return adjustFeatureModuleForState(options, featureModulePath)(tree, context); }); projectChains.push((tree, context) => { return general_1.updatePackageForNgrx(tree, `apps/${projectName}/package.json`); }); } else { projectChains.push((tree, context) => { return adjustBarrelIndex(type, options, barrelIndex)(tree, context); }); projectChains.push((tree, context) => { return addToFeature(type, options, prefixPath, tree, "_index")(tree, context); }); projectChains.push((tree, context) => { return adjustFeatureModule(type, options, featureModulePath)(tree, context); }); } } } else { projectChains.push(schematics_1.noop()); } return schematics_1.chain([ general_1.prerun(), (tree, context) => // for entire workspace usage // no projects and no specific platforms specified !options.projects && platforms.length === 0 ? addToFeature(type, options, "libs", tree)(tree, context) : schematics_1.noop()(tree, context), // adjust libs barrel (tree, context) => !options.projects && platforms.length === 0 ? adjustBarrel(type, options, "libs")(tree, context) : schematics_1.noop()(tree, context), // add index barrel if needed (tree, context) => options.needsIndex ? addToFeature(type, options, "libs", tree, "_index")(tree, context) : schematics_1.noop()(tree, context), // adjust feature module metadata if needed (tree, context) => !options.projects && platforms.length === 0 ? adjustModule(type, options, "libs")(tree, context) : schematics_1.noop()(tree, context), /** * NATIVESCRIPT **/ // add for {N} (tree, context) => !options.projects && targetPlatforms.nativescript ? addToFeature(type, options, "xplat/nativescript", tree)(tree, context) : schematics_1.noop()(tree, context), // adjust {N} barrel (tree, context) => !options.projects && targetPlatforms.nativescript ? adjustBarrel(type, options, "xplat/nativescript")(tree, context) : schematics_1.noop()(tree, context), // add index barrel if needed (tree, context) => options.needsIndex ? addToFeature(type, options, "xplat/nativescript", tree, "_index")(tree, context) : schematics_1.noop()(tree, context), // adjust feature module metadata if needed (tree, context) => !options.projects && targetPlatforms.nativescript ? adjustModule(type, options, "xplat/nativescript")(tree, context) : schematics_1.noop()(tree, context), /** * WEB **/ // add for web (tree, context) => !options.projects && targetPlatforms.web ? addToFeature(type, options, "xplat/web", tree)(tree, context) : schematics_1.noop()(tree, context), // adjust web barrel (tree, context) => !options.projects && targetPlatforms.web ? adjustBarrel(type, options, "xplat/web")(tree, context) : schematics_1.noop()(tree, context), // add index barrel if needed (tree, context) => options.needsIndex ? addToFeature(type, options, "xplat/web", tree, "_index")(tree, context) : schematics_1.noop()(tree, context), // adjust feature module metadata if needed (tree, context) => !options.projects && targetPlatforms.web ? adjustModule(type, options, "xplat/web")(tree, context) : schematics_1.noop()(tree, context), /** * IONIC **/ // add for ionic (tree, context) => !options.projects && targetPlatforms.ionic ? addToFeature(type, options, "xplat/ionic", tree)(tree, context) : schematics_1.noop()(tree, context), // adjust ionic barrel (tree, context) => !options.projects && targetPlatforms.ionic ? adjustBarrel(type, options, "xplat/ionic")(tree, context) : schematics_1.noop()(tree, context), // add index barrel if needed (tree, context) => options.needsIndex ? addToFeature(type, options, "xplat/ionic", tree, "_index")(tree, context) : schematics_1.noop()(tree, context), // adjust feature module metadata if needed (tree, context) => !options.projects && targetPlatforms.nativescript ? adjustModule(type, options, "xplat/ionic")(tree, context) : schematics_1.noop()(tree, context), /** * ELECTRON **/ // add for electron (tree, context) => !options.projects && targetPlatforms.electron ? addToFeature(type, options, "xplat/electron", tree)(tree, context) : schematics_1.noop()(tree, context), // adjust electron barrel (tree, context) => !options.projects && targetPlatforms.electron ? adjustBarrel(type, options, "xplat/electron")(tree, context) : schematics_1.noop()(tree, context), // add index barrel if needed (tree, context) => options.needsIndex ? addToFeature(type, options, "xplat/electron", tree, "_index")(tree, context) : schematics_1.noop()(tree, context), // adjust feature module metadata if needed (tree, context) => !options.projects && targetPlatforms.electron ? adjustModule(type, options, "xplat/electron")(tree, context) : schematics_1.noop()(tree, context), /** * NEST **/ // add for nest (tree, context) => !options.projects && targetPlatforms.nest ? addToFeature(type, options, "xplat/nest", tree)(tree, context) : schematics_1.noop()(tree, context), // adjust nest barrel (tree, context) => !options.projects && targetPlatforms.nest ? adjustBarrel(type, options, "xplat/nest")(tree, context) : schematics_1.noop()(tree, context), // add index barrel if needed (tree, context) => options.needsIndex ? addToFeature(type, options, "xplat/nest", tree, "_index")(tree, context) : schematics_1.noop()(tree, context), // adjust feature module metadata if needed (tree, context) => !options.projects && targetPlatforms.nest ? adjustModule(type, options, "xplat/nest")(tree, context) : schematics_1.noop()(tree, context), // project handling ...projectChains, // dependency updates (tree, context) => !options.projects && type === "state" ? // ensure ngrx dependencies are added to root package general_1.updatePackageForNgrx(tree) : schematics_1.noop()(tree, context), options.skipFormat ? schematics_1.noop() : format_files_1.formatFiles(options) ]); } exports.generate = generate; function getFeatureName(options) { let featureName; if (options.feature) { featureName = options.feature.toLowerCase(); } if (!featureName) { if (options.projects) { // default to shared barrel featureName = "shared"; } else { // default to ui barrel featureName = "ui"; } } return featureName; } exports.getFeatureName = getFeatureName; function addToFeature(type, options, prefixPath, tree, extra = "", forSubFolder) { let featureName = getFeatureName(options); options.needsIndex = false; // reset let featurePath; if (shouldTargetCoreBarrel(type, featureName)) { // services and/or state should never be generated in shared or ui features // therefore place in core (since they are service level) featureName = "core"; featurePath = `${prefixPath}/${featureName}`; } else { featurePath = `${prefixPath}/features/${featureName}`; } const featureModulePath = `${featurePath}/${featureName}.module.ts`; let moveTo; if (extra === "_base" || extra === "_base_index") { // always in libs moveTo = `libs/features/${featureName}/base`; } else { moveTo = `${featurePath}/${type}${type === "state" ? "" : "s"}`; if (!tree.exists(featureModulePath)) { let optionName; if (prefixPath !== "libs") { // parse platform from prefix const parts = prefixPath.split("/"); if (parts.length > 1) { optionName = parts[1]; } } throw new Error(errors_1.needFeatureModuleError(featureModulePath, featureName, optionName)); } } if (forSubFolder && options.subFolder) { moveTo += `/${options.subFolder}`; } // console.log('moveTo:', moveTo); const indexPath = `${moveTo}/index.ts`; if ((extra === "_index" || extra === "_base_index") && tree.exists(indexPath)) { // already has an index barrel return schematics_1.noop(); } else { return schematics_1.branchAndMerge(schematics_1.mergeWith(schematics_1.apply(schematics_1.url(`./${extra}_files`), [ schematics_1.template(Object.assign({}, options, { name: options.name.toLowerCase(), forSubFolder, npmScope: general_1.getNpmScope(), prefix: general_1.getPrefix(), dot: ".", utils: general_1.stringUtils })), schematics_1.move(moveTo) ]))); } } exports.addToFeature = addToFeature; function isFeatureInGeneralBarrel(featureName) { // 'shared' barrel is for app specific shared components, pipes, directives (not service level features) // 'ui' barrel is for entire workspace ui related sharing of components, pipes, directives (not service level features) return featureName === "shared" || featureName === "ui"; } exports.isFeatureInGeneralBarrel = isFeatureInGeneralBarrel; function shouldTargetCoreBarrel(type, featureName) { // when service or state is being generated with no options, it falls back to shared/ui // services and state should never be generated in shared or ui features // therefore target core barrel return ((type === "service" || type === "state") && isFeatureInGeneralBarrel(featureName)); } exports.shouldTargetCoreBarrel = shouldTargetCoreBarrel; function adjustBarrel(type, options, prefix) { let featureName = getFeatureName(options); let barrelIndexPath; if (shouldTargetCoreBarrel(type, featureName)) { if (type === "state") { barrelIndexPath = `${prefix}/core/index.ts`; } else { barrelIndexPath = `${prefix}/core/${type}s/index.ts`; } } else { if (type === "state") { barrelIndexPath = `${prefix}/features/${featureName}/index.ts`; } else { barrelIndexPath = `${prefix}/features/${featureName}/${type}s/index.ts`; } } if (type === "state") { return adjustBarrelIndexForType(type, options, barrelIndexPath); } else { return adjustBarrelIndex(type, options, barrelIndexPath); } } exports.adjustBarrel = adjustBarrel; function adjustBarrelIndex(type, options, indexFilePath, inSubFolder, isBase, importIfSubFolder) { return (host) => { // console.log('adjustBarrelIndex:', indexFilePath); // console.log('host.exists(indexFilePath):', host.exists(indexFilePath)); if (host.exists(indexFilePath)) { const indexSource = host.read(indexFilePath).toString("utf-8"); const indexSourceFile = ts.createSourceFile(indexFilePath, indexSource, ts.ScriptTarget.Latest, true); const changes = []; const name = options.name.toLowerCase(); if (!isBase) { // add to barrel collection if (importIfSubFolder && options.subFolder) { // import collection from subfolder const symbolName = `${general_1.stringUtils .sanitize(options.subFolder) .toUpperCase()}_${type.toUpperCase()}S`; changes.push(...ast_1.addGlobal(indexSourceFile, indexFilePath, `import { ${symbolName} } from './${options.subFolder}';`), ...ast_1.addToCollection(indexSourceFile, indexFilePath, `...${symbolName}`, " ")); } else { const symbolName = `${general_1.stringUtils.classify(name)}${general_1.stringUtils.capitalize(type)}`; changes.push(...ast_1.addGlobal(indexSourceFile, indexFilePath, `import { ${symbolName} } from './${inSubFolder ? `${name}/` : ""}${name}.${type}';`), ...ast_1.addToCollection(indexSourceFile, indexFilePath, symbolName, " ")); } } if (type === "component" || type === "service" || type === 'pipe') { // export symbol from barrel if ((isBase || importIfSubFolder) && options.subFolder) { changes.push(...ast_1.addGlobal(indexSourceFile, indexFilePath, `export * from './${options.subFolder}';`, true)); } else { const subFolder = inSubFolder ? `${name}/` : ""; changes.push(...ast_1.addGlobal(indexSourceFile, indexFilePath, `export * from './${subFolder}${name}.${isBase ? "base-" : ""}${type}';`, true)); } } ast_1.insert(host, indexFilePath, changes); } else { options.needsIndex = true; } return host; }; } exports.adjustBarrelIndex = adjustBarrelIndex; function adjustBarrelIndexForType(type, options, indexFilePath) { return (host) => { if (host.exists(indexFilePath)) { const indexSource = host.read(indexFilePath).toString("utf-8"); const indexSourceFile = ts.createSourceFile(indexFilePath, indexSource, ts.ScriptTarget.Latest, true); const changes = []; changes.push(...ast_1.addGlobal(indexSourceFile, indexFilePath, `export * from './${type}';`, true)); ast_1.insert(host, indexFilePath, changes); } else { options.needsIndex = true; } return host; }; } exports.adjustBarrelIndexForType = adjustBarrelIndexForType; function adjustModule(type, options, prefixPath) { let featureName = getFeatureName(options); let featurePath; if (shouldTargetCoreBarrel(type, featureName)) { featureName = "core"; featurePath = `${prefixPath}/${featureName}`; } else { featurePath = `${prefixPath}/features/${featureName}`; } const featureModulePath = `${featurePath}/${featureName}.module.ts`; if (type === "state") { return adjustFeatureModuleForState(options, featureModulePath); } else { return adjustFeatureModule(type, options, featureModulePath); } } exports.adjustModule = adjustModule; function adjustFeatureModule(type, options, modulePath) { return (host) => { // console.log('adjustFeatureModule:', modulePath); if (host.exists(modulePath)) { const moduleSource = host.read(modulePath).toString("utf-8"); const moduleSourceFile = ts.createSourceFile(modulePath, moduleSource, ts.ScriptTarget.Latest, true); const changes = []; let featureName; if (options.feature) { featureName = general_1.stringUtils.sanitize(options.feature).toUpperCase(); } else { // default collections if (type === "service") { featureName = "CORE"; } else { if (modulePath.indexOf("apps") > -1) { // app specific shared featureName = "SHARED"; } else { // workspace cross platform ui libraries featureName = "UI"; } } } let collectionName; switch (type) { case "component": collectionName = `${featureName}_COMPONENTS`; break; case "directive": collectionName = `${featureName}_DIRECTIVES`; break; case "pipe": collectionName = `${featureName}_PIPES`; break; case "service": collectionName = `${featureName}_PROVIDERS`; break; } // console.log('collectionName:', collectionName); // console.log('moduleSource:', moduleSource); if (moduleSource.indexOf(collectionName) > -1) { // already handled return host; } else { // add to module changes.push(...ast_1.addGlobal(moduleSourceFile, modulePath, `import { ${collectionName} } from './${type}s';`)); if (type === "service") { changes.push(...ast_1.addProviderToModule(moduleSourceFile, modulePath, `...${collectionName}`)); } else { changes.push(...ast_1.addDeclarationToModule(moduleSourceFile, modulePath, `...${collectionName}`), ...ast_1._addSymbolToNgModuleMetadata(moduleSourceFile, modulePath, "exports", `...${collectionName}`)); } ast_1.insert(host, modulePath, changes); } } return host; }; } exports.adjustFeatureModule = adjustFeatureModule; function adjustFeatureModuleForState(options, modulePath) { return (host) => { // console.log('adjustFeatureModuleForState:', modulePath); if (host.exists(modulePath)) { const moduleSource = host.read(modulePath).toString("utf-8"); const moduleSourceFile = ts.createSourceFile(modulePath, moduleSource, ts.ScriptTarget.Latest, true); // console.log('moduleSource:', moduleSource); const isInLibs = modulePath.indexOf("libs") === 0; const name = options.name.toLowerCase(); const changes = []; if (moduleSource.indexOf("StoreModule") === -1) { changes.push(...ast_1.addGlobal(moduleSourceFile, modulePath, `import { StoreModule } from '@ngrx/store';`)); } if (moduleSource.indexOf("EffectsModule") === -1) { changes.push(...ast_1.addGlobal(moduleSourceFile, modulePath, `import { EffectsModule } from '@ngrx/effects';`)); } if (moduleSource.indexOf("ngrx-store-freeze") === -1) { changes.push(...ast_1.addGlobal(moduleSourceFile, modulePath, `import { storeFreeze } from 'ngrx-store-freeze';`)); } changes.push(...ast_1.addGlobal(moduleSourceFile, modulePath, `import { ${general_1.stringUtils.classify(name)}Effects } from './state/${name}.effects';`), ...ast_1.addGlobal(moduleSourceFile, modulePath, `import { ${name}Reducer } from './state/${name}.reducer';`), ...ast_1.addGlobal(moduleSourceFile, modulePath, `import { ${general_1.stringUtils.classify(name)}State } from './state/${name}.state';`)); if (options.root) { if (moduleSource.indexOf("environments/environment") === -1) { const envFrom = isInLibs ? "./environments/environment" : `@${general_1.getNpmScope()}/core`; changes.push(...ast_1.addGlobal(moduleSourceFile, modulePath, `import { environment } from '${envFrom}';`)); } changes.push(...ast_1.addImportToModule(moduleSourceFile, modulePath, `StoreModule.forRoot( { ${name}: ${name}Reducer }, { initialState: { ${name}: ${general_1.stringUtils.classify(name)}State.initialState }, metaReducers: !environment.production ? [storeFreeze] : [] } )`), ...ast_1.addImportToModule(moduleSourceFile, modulePath, `EffectsModule.forRoot([${general_1.stringUtils.classify(name)}Effects])`), ...ast_1.addProviderToModule(moduleSourceFile, modulePath, `${general_1.stringUtils.classify(name)}Effects`)); } else { // feature state changes.push(...ast_1.addImportToModule(moduleSourceFile, modulePath, `StoreModule.forFeature('${name}', ${name}Reducer, { initialState: ${general_1.stringUtils.classify(name)}State.initialState })`), ...ast_1.addImportToModule(moduleSourceFile, modulePath, `EffectsModule.forFeature([${general_1.stringUtils.classify(name)}Effects])`), ...ast_1.addProviderToModule(moduleSourceFile, modulePath, `${general_1.stringUtils.classify(name)}Effects`)); } ast_1.insert(host, modulePath, changes); } return host; }; } exports.adjustFeatureModuleForState = adjustFeatureModuleForState; //# sourceMappingURL=generator.js.map