@nstudio/schematics
Version:
Cross-platform (xplat) tools for Nx workspaces.
529 lines • 25.1 kB
JavaScript
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
;