@ngrx/schematics
Version:
NgRx Schematics for Angular
411 lines • 18 kB
JavaScript
"use strict";
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findBootstrapApplicationCall = exports.addFunctionalProvidersToStandaloneBootstrap = exports.callsProvidersFunction = void 0;
// copied from https://github.com/angular/angular-cli/blob/17.3.x/packages/schematics/angular/private/standalone.ts
var schematics_1 = require("@angular-devkit/schematics");
var path_1 = require("path");
var ast_utils_1 = require("./ast-utils");
var change_1 = require("./change");
var ts = require("typescript");
/**
* Checks whether a providers function is being called in a `bootstrapApplication` call.
* @param tree File tree of the project.
* @param filePath Path of the file in which to check.
* @param functionName Name of the function to search for.
* @deprecated Private utility that will be removed. Use `addRootImport` or `addRootProvider` from
* `@schematics/angular/utility` instead.
*/
function callsProvidersFunction(tree, filePath, functionName) {
var sourceFile = createSourceFile(tree, filePath);
var bootstrapCall = findBootstrapApplicationCall(sourceFile);
var appConfig = bootstrapCall
? findAppConfig(bootstrapCall, tree, filePath)
: null;
var providersLiteral = appConfig
? findProvidersLiteral(appConfig.node)
: null;
return !!(providersLiteral === null || providersLiteral === void 0 ? void 0 : providersLiteral.elements.some(function (el) {
return ts.isCallExpression(el) &&
ts.isIdentifier(el.expression) &&
el.expression.text === functionName;
}));
}
exports.callsProvidersFunction = callsProvidersFunction;
/**
* Adds a providers function call to the `bootstrapApplication` call.
* @param tree File tree of the project.
* @param filePath Path to the file that should be updated.
* @param functionName Name of the function that should be called.
* @param importPath Path from which to import the function.
* @param args Arguments to use when calling the function.
* @return The file path that the provider was added to.
* @deprecated Private utility that will be removed. Use `addRootImport` or `addRootProvider` from
* `@schematics/angular/utility` instead.
*/
function addFunctionalProvidersToStandaloneBootstrap(tree, filePath, functionName, importPath, args) {
if (args === void 0) { args = []; }
var sourceFile = createSourceFile(tree, filePath);
var bootstrapCall = findBootstrapApplicationCall(sourceFile);
var addImports = function (file, recorder) {
var change = (0, ast_utils_1.insertImport)(file, file.getText(), functionName, importPath);
if (change instanceof change_1.InsertChange) {
recorder.insertLeft(change.pos, change.toAdd);
}
};
if (!bootstrapCall) {
throw new schematics_1.SchematicsException("Could not find bootstrapApplication call in ".concat(filePath));
}
var providersCall = ts.factory.createCallExpression(ts.factory.createIdentifier(functionName), undefined, args);
// If there's only one argument, we have to create a new object literal.
if (bootstrapCall.arguments.length === 1) {
var recorder_1 = tree.beginUpdate(filePath);
addNewAppConfigToCall(bootstrapCall, providersCall, recorder_1);
addImports(sourceFile, recorder_1);
tree.commitUpdate(recorder_1);
return filePath;
}
// If the config is a `mergeApplicationProviders` call, add another config to it.
if (isMergeAppConfigCall(bootstrapCall.arguments[1])) {
var recorder_2 = tree.beginUpdate(filePath);
addNewAppConfigToCall(bootstrapCall.arguments[1], providersCall, recorder_2);
addImports(sourceFile, recorder_2);
tree.commitUpdate(recorder_2);
return filePath;
}
// Otherwise attempt to merge into the current config.
var appConfig = findAppConfig(bootstrapCall, tree, filePath);
if (!appConfig) {
throw new schematics_1.SchematicsException("Could not statically analyze config in bootstrapApplication call in ".concat(filePath));
}
var configFilePath = appConfig.filePath, config = appConfig.node;
var recorder = tree.beginUpdate(configFilePath);
var providersLiteral = findProvidersLiteral(config);
addImports(config.getSourceFile(), recorder);
if (providersLiteral) {
// If there's a `providers` array, add the import to it.
addElementToArray(providersLiteral, providersCall, recorder);
}
else {
// Otherwise add a `providers` array to the existing object literal.
addProvidersToObjectLiteral(config, providersCall, recorder);
}
tree.commitUpdate(recorder);
return configFilePath;
}
exports.addFunctionalProvidersToStandaloneBootstrap = addFunctionalProvidersToStandaloneBootstrap;
/**
* Finds the call to `bootstrapApplication` within a file.
* @deprecated Private utility that will be removed. Use `addRootImport` or `addRootProvider` from
* `@schematics/angular/utility` instead.
*/
function findBootstrapApplicationCall(sourceFile) {
var localName = findImportLocalName(sourceFile, 'bootstrapApplication', '@angular/platform-browser');
if (!localName) {
return null;
}
var result = null;
sourceFile.forEachChild(function walk(node) {
if (ts.isCallExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === localName) {
result = node;
}
if (!result) {
node.forEachChild(walk);
}
});
return result;
}
exports.findBootstrapApplicationCall = findBootstrapApplicationCall;
/** Finds the `providers` array literal within an application config. */
function findProvidersLiteral(config) {
var e_1, _a;
try {
for (var _b = __values(config.properties), _c = _b.next(); !_c.done; _c = _b.next()) {
var prop = _c.value;
if (ts.isPropertyAssignment(prop) &&
ts.isIdentifier(prop.name) &&
prop.name.text === 'providers' &&
ts.isArrayLiteralExpression(prop.initializer)) {
return prop.initializer;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
return null;
}
/**
* Resolves the node that defines the app config from a bootstrap call.
* @param bootstrapCall Call for which to resolve the config.
* @param tree File tree of the project.
* @param filePath File path of the bootstrap call.
*/
function findAppConfig(bootstrapCall, tree, filePath) {
if (bootstrapCall.arguments.length > 1) {
var config = bootstrapCall.arguments[1];
if (ts.isObjectLiteralExpression(config)) {
return { filePath: filePath, node: config };
}
if (ts.isIdentifier(config)) {
return resolveAppConfigFromIdentifier(config, tree, filePath);
}
}
return null;
}
/**
* Resolves the app config from an identifier referring to it.
* @param identifier Identifier referring to the app config.
* @param tree File tree of the project.
* @param bootstapFilePath Path of the bootstrap call.
*/
function resolveAppConfigFromIdentifier(identifier, tree, bootstapFilePath) {
var e_2, _a, e_3, _b;
var _c;
var sourceFile = identifier.getSourceFile();
try {
for (var _d = __values(sourceFile.statements), _e = _d.next(); !_e.done; _e = _d.next()) {
var node = _e.value;
// Only look at relative imports. This will break if the app uses a path
// mapping to refer to the import, but in order to resolve those, we would
// need knowledge about the entire program.
if (!ts.isImportDeclaration(node) ||
!((_c = node.importClause) === null || _c === void 0 ? void 0 : _c.namedBindings) ||
!ts.isNamedImports(node.importClause.namedBindings) ||
!ts.isStringLiteralLike(node.moduleSpecifier) ||
!node.moduleSpecifier.text.startsWith('.')) {
continue;
}
try {
for (var _f = (e_3 = void 0, __values(node.importClause.namedBindings.elements)), _g = _f.next(); !_g.done; _g = _f.next()) {
var specifier = _g.value;
if (specifier.name.text !== identifier.text) {
continue;
}
// Look for a variable with the imported name in the file. Note that ideally we would use
// the type checker to resolve this, but we can't because these utilities are set up to
// operate on individual files, not the entire program.
var filePath = (0, path_1.join)((0, path_1.dirname)(bootstapFilePath), node.moduleSpecifier.text + '.ts');
var importedSourceFile = createSourceFile(tree, filePath);
var resolvedVariable = findAppConfigFromVariableName(importedSourceFile, (specifier.propertyName || specifier.name).text);
if (resolvedVariable) {
return { filePath: filePath, node: resolvedVariable };
}
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
}
finally { if (e_3) throw e_3.error; }
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
}
finally { if (e_2) throw e_2.error; }
}
var variableInSameFile = findAppConfigFromVariableName(sourceFile, identifier.text);
return variableInSameFile
? { filePath: bootstapFilePath, node: variableInSameFile }
: null;
}
/**
* Finds an app config within the top-level variables of a file.
* @param sourceFile File in which to search for the config.
* @param variableName Name of the variable containing the config.
*/
function findAppConfigFromVariableName(sourceFile, variableName) {
var e_4, _a, e_5, _b;
try {
for (var _c = __values(sourceFile.statements), _d = _c.next(); !_d.done; _d = _c.next()) {
var node = _d.value;
if (ts.isVariableStatement(node)) {
try {
for (var _e = (e_5 = void 0, __values(node.declarationList.declarations)), _f = _e.next(); !_f.done; _f = _e.next()) {
var decl = _f.value;
if (ts.isIdentifier(decl.name) &&
decl.name.text === variableName &&
decl.initializer &&
ts.isObjectLiteralExpression(decl.initializer)) {
return decl.initializer;
}
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
}
finally { if (e_5) throw e_5.error; }
}
}
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_4) throw e_4.error; }
}
return null;
}
/**
* Finds the local name of an imported symbol. Could be the symbol name itself or its alias.
* @param sourceFile File within which to search for the import.
* @param name Actual name of the import, not its local alias.
* @param moduleName Name of the module from which the symbol is imported.
*/
function findImportLocalName(sourceFile, name, moduleName) {
var e_6, _a, e_7, _b;
try {
for (var _c = __values(sourceFile.statements), _d = _c.next(); !_d.done; _d = _c.next()) {
var node = _d.value;
// Only look for top-level imports.
if (!ts.isImportDeclaration(node) ||
!ts.isStringLiteral(node.moduleSpecifier) ||
node.moduleSpecifier.text !== moduleName) {
continue;
}
// Filter out imports that don't have the right shape.
if (!node.importClause ||
!node.importClause.namedBindings ||
!ts.isNamedImports(node.importClause.namedBindings)) {
continue;
}
try {
// Look through the elements of the declaration for the specific import.
for (var _e = (e_7 = void 0, __values(node.importClause.namedBindings.elements)), _f = _e.next(); !_f.done; _f = _e.next()) {
var element = _f.value;
if ((element.propertyName || element.name).text === name) {
// The local name is always in `name`.
return element.name.text;
}
}
}
catch (e_7_1) { e_7 = { error: e_7_1 }; }
finally {
try {
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
}
finally { if (e_7) throw e_7.error; }
}
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
}
finally { if (e_6) throw e_6.error; }
}
return null;
}
/** Creates a source file from a file path within a project. */
function createSourceFile(tree, filePath) {
return ts.createSourceFile(filePath, tree.readText(filePath), ts.ScriptTarget.Latest, true);
}
/**
* Creates a new app config object literal and adds it to a call expression as an argument.
* @param call Call to which to add the config.
* @param expression Expression that should inserted into the new config.
* @param recorder Recorder to which to log the change.
*/
function addNewAppConfigToCall(call, expression, recorder) {
var newCall = ts.factory.updateCallExpression(call, call.expression, call.typeArguments, __spreadArray(__spreadArray([], __read(call.arguments), false), [
ts.factory.createObjectLiteralExpression([
ts.factory.createPropertyAssignment('providers', ts.factory.createArrayLiteralExpression([expression])),
], true),
], false));
recorder.remove(call.getStart(), call.getWidth());
recorder.insertRight(call.getStart(), ts
.createPrinter()
.printNode(ts.EmitHint.Unspecified, newCall, call.getSourceFile()));
}
/**
* Adds an element to an array literal expression.
* @param node Array to which to add the element.
* @param element Element to be added.
* @param recorder Recorder to which to log the change.
*/
function addElementToArray(node, element, recorder) {
var newLiteral = ts.factory.updateArrayLiteralExpression(node, __spreadArray(__spreadArray([], __read(node.elements), false), [
element,
], false));
recorder.remove(node.getStart(), node.getWidth());
recorder.insertRight(node.getStart(), ts
.createPrinter()
.printNode(ts.EmitHint.Unspecified, newLiteral, node.getSourceFile()));
}
/**
* Adds a `providers` property to an object literal.
* @param node Literal to which to add the `providers`.
* @param expression Provider that should be part of the generated `providers` array.
* @param recorder Recorder to which to log the change.
*/
function addProvidersToObjectLiteral(node, expression, recorder) {
var newOptionsLiteral = ts.factory.updateObjectLiteralExpression(node, __spreadArray(__spreadArray([], __read(node.properties), false), [
ts.factory.createPropertyAssignment('providers', ts.factory.createArrayLiteralExpression([expression])),
], false));
recorder.remove(node.getStart(), node.getWidth());
recorder.insertRight(node.getStart(), ts
.createPrinter()
.printNode(ts.EmitHint.Unspecified, newOptionsLiteral, node.getSourceFile()));
}
/** Checks whether a node is a call to `mergeApplicationConfig`. */
function isMergeAppConfigCall(node) {
if (!ts.isCallExpression(node)) {
return false;
}
var localName = findImportLocalName(node.getSourceFile(), 'mergeApplicationConfig', '@angular/core');
return (!!localName &&
ts.isIdentifier(node.expression) &&
node.expression.text === localName);
}
//# sourceMappingURL=standalone.js.map