@nstudio/angular 15.0.4-rc.1 → 16.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/utils/ast.js CHANGED
@@ -1,24 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getDecoratorPropertyValueNode = exports.readBootstrapInfo = exports.addEntryComponents = exports.addDeclarationToModule = exports.addProviderToModule = exports.addRoute = exports.getBootstrapComponent = exports.addImportToTestBed = exports.addImportToModule = exports.removeFromNgModule = exports.addSymbolToNgModuleMetadata = exports.addToCollection = void 0;
4
- const ts = require("typescript");
3
+ exports.getTsSourceFile = exports.getDecoratorPropertyValueNode = exports.readBootstrapInfo = exports.addEntryComponents = exports.addDeclarationToModule = exports.addProviderToComponent = exports.addProviderToModule = exports.addProviderToBootstrapApplication = exports.addRouteToNgModule = exports.getBootstrapComponent = exports.replaceIntoToTestBed = exports.addDeclarationsToTestBed = exports.addImportToTestBed = exports.addImportToModule = exports.addImportToPipe = exports.addImportToDirective = exports.addImportToComponent = exports.removeFromNgModule = exports._addSymbolToNgModuleMetadata = exports.getDecoratorMetadata = exports.isStandalone = exports.addToCollection = void 0;
4
+ const ensure_typescript_1 = require("@nx/js/src/utils/typescript/ensure-typescript");
5
5
  const typescript_1 = require("nx/src/utils/typescript");
6
- const path = require("path");
7
- const xplat_utils_1 = require("@nstudio/xplat-utils");
8
- const xplat_1 = require("@nstudio/xplat");
9
- function addToCollection(source, barrelIndexPath, symbolName, insertSpaces = '') {
6
+ const ts = require("typescript");
7
+ const js_1 = require("@nx/js");
8
+ const path_1 = require("path");
9
+ const devkit_1 = require("@nx/devkit");
10
+ let tsModule;
11
+ function addToCollection(tree, source, barrelIndexPath, symbolName, insertSpaces = '') {
10
12
  const collection = getCollection(source);
11
13
  if (!collection)
12
- return [];
14
+ return source;
13
15
  // if (!collection) return [new NoopChange()];
14
16
  // return [new NoopChange()];
17
+ console.log('collection.hasTrailingComma:', collection.hasTrailingComma);
15
18
  if (collection.hasTrailingComma || collection.length === 0) {
16
- return [new xplat_1.InsertChange(barrelIndexPath, collection.end, symbolName)];
19
+ return (0, js_1.insertChange)(tree, source, barrelIndexPath, collection.end, symbolName);
17
20
  }
18
21
  else {
19
- return [
20
- new xplat_1.InsertChange(barrelIndexPath, collection.end, `,\n${insertSpaces}${symbolName}`),
21
- ];
22
+ return (0, js_1.insertChange)(tree, source, barrelIndexPath, collection.end, `,\n${insertSpaces}${symbolName}`);
22
23
  }
23
24
  }
24
25
  exports.addToCollection = addToCollection;
@@ -36,10 +37,13 @@ function getCollection(source) {
36
37
  return null;
37
38
  }
38
39
  function _angularImportsFromNode(node, _sourceFile) {
40
+ if (!tsModule) {
41
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
42
+ }
39
43
  const ms = node.moduleSpecifier;
40
44
  let modulePath;
41
45
  switch (ms.kind) {
42
- case ts.SyntaxKind.StringLiteral:
46
+ case tsModule.SyntaxKind.StringLiteral:
43
47
  modulePath = ms.text;
44
48
  break;
45
49
  default:
@@ -55,10 +59,10 @@ function _angularImportsFromNode(node, _sourceFile) {
55
59
  }
56
60
  else if (node.importClause.namedBindings) {
57
61
  const nb = node.importClause.namedBindings;
58
- if (nb.kind == ts.SyntaxKind.NamespaceImport) {
62
+ if (nb.kind == tsModule.SyntaxKind.NamespaceImport) {
59
63
  // This is of the form `import * as name from 'path'`. Return `name.`.
60
64
  return {
61
- [nb.name.text + '.']: modulePath,
65
+ [`${nb.name.text}.`]: modulePath,
62
66
  };
63
67
  }
64
68
  else {
@@ -79,8 +83,21 @@ function _angularImportsFromNode(node, _sourceFile) {
79
83
  return {};
80
84
  }
81
85
  }
86
+ /**
87
+ * Check if the Component, Directive or Pipe is standalone
88
+ * @param sourceFile TS Source File containing the token to check
89
+ * @param decoratorName The type of decorator to check (Component, Directive, Pipe)
90
+ */
91
+ function isStandalone(sourceFile, decoratorName) {
92
+ const decoratorMetadata = getDecoratorMetadata(sourceFile, decoratorName, '@angular/core');
93
+ return decoratorMetadata.some((node) => node.getText().includes('standalone: true'));
94
+ }
95
+ exports.isStandalone = isStandalone;
82
96
  function getDecoratorMetadata(source, identifier, module) {
83
- const angularImports = (0, typescript_1.findNodes)(source, ts.SyntaxKind.ImportDeclaration)
97
+ if (!tsModule) {
98
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
99
+ }
100
+ const angularImports = (0, typescript_1.findNodes)(source, tsModule.SyntaxKind.ImportDeclaration)
84
101
  .map((node) => _angularImportsFromNode(node, source))
85
102
  .reduce((acc, current) => {
86
103
  for (const key of Object.keys(current)) {
@@ -88,60 +105,65 @@ function getDecoratorMetadata(source, identifier, module) {
88
105
  }
89
106
  return acc;
90
107
  }, {});
91
- return (0, xplat_1.getSourceNodes)(source)
108
+ return (0, js_1.getSourceNodes)(source)
92
109
  .filter((node) => {
93
- return (node.kind == ts.SyntaxKind.Decorator &&
94
- node.expression.kind == ts.SyntaxKind.CallExpression);
110
+ return (node.kind == tsModule.SyntaxKind.Decorator &&
111
+ node.expression.kind ==
112
+ tsModule.SyntaxKind.CallExpression);
95
113
  })
96
114
  .map((node) => node.expression)
97
115
  .filter((expr) => {
98
- if (expr.expression.kind == ts.SyntaxKind.Identifier) {
116
+ if (expr.expression.kind == tsModule.SyntaxKind.Identifier) {
99
117
  const id = expr.expression;
100
118
  return (id.getFullText(source) == identifier &&
101
119
  angularImports[id.getFullText(source)] === module);
102
120
  }
103
- else if (expr.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
121
+ else if (expr.expression.kind == tsModule.SyntaxKind.PropertyAccessExpression) {
104
122
  // This covers foo.NgModule when importing * as foo.
105
123
  const paExpr = expr.expression;
106
124
  // If the left expression is not an identifier, just give up at that point.
107
- if (paExpr.expression.kind !== ts.SyntaxKind.Identifier) {
125
+ if (paExpr.expression.kind !== tsModule.SyntaxKind.Identifier) {
108
126
  return false;
109
127
  }
110
128
  const id = paExpr.name.text;
111
129
  const moduleId = paExpr.expression.getText(source);
112
- return id === identifier && angularImports[moduleId + '.'] === module;
130
+ return id === identifier && angularImports[`${moduleId}.`] === module;
113
131
  }
114
132
  return false;
115
133
  })
116
134
  .filter((expr) => expr.arguments[0] &&
117
- expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression)
135
+ expr.arguments[0].kind == tsModule.SyntaxKind.ObjectLiteralExpression)
118
136
  .map((expr) => expr.arguments[0]);
119
137
  }
120
- function addSymbolToNgModuleMetadata(source, ngModulePath, metadataField, expression) {
121
- const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
138
+ exports.getDecoratorMetadata = getDecoratorMetadata;
139
+ function _addSymbolToDecoratorMetadata(host, source, filePath, metadataField, expression, decoratorName) {
140
+ const nodes = getDecoratorMetadata(source, decoratorName, '@angular/core');
122
141
  let node = nodes[0]; // tslint:disable-line:no-any
123
142
  // Find the decorator declaration.
124
143
  if (!node) {
125
- return [];
144
+ return source;
145
+ }
146
+ if (!tsModule) {
147
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
126
148
  }
127
149
  // Get all the children property assignment of object literals.
128
150
  const matchingProperties = node.properties
129
- .filter((prop) => prop.kind == ts.SyntaxKind.PropertyAssignment)
151
+ .filter((prop) => prop.kind == tsModule.SyntaxKind.PropertyAssignment)
130
152
  // Filter out every fields that's not "metadataField". Also handles string literals
131
153
  // (but not expressions).
132
154
  .filter((prop) => {
133
155
  const name = prop.name;
134
156
  switch (name.kind) {
135
- case ts.SyntaxKind.Identifier:
157
+ case tsModule.SyntaxKind.Identifier:
136
158
  return name.getText(source) == metadataField;
137
- case ts.SyntaxKind.StringLiteral:
159
+ case tsModule.SyntaxKind.StringLiteral:
138
160
  return name.text == metadataField;
139
161
  }
140
162
  return false;
141
163
  });
142
164
  // Get the last node of the array literal.
143
165
  if (!matchingProperties) {
144
- return [];
166
+ return source;
145
167
  }
146
168
  if (matchingProperties.length == 0) {
147
169
  // We haven't found the field in the metadata declaration. Insert a new field.
@@ -164,13 +186,12 @@ function addSymbolToNgModuleMetadata(source, ngModulePath, metadataField, expres
164
186
  toInsert = `, ${metadataField}: [${expression}]`;
165
187
  }
166
188
  }
167
- const newMetadataProperty = new xplat_1.InsertChange(ngModulePath, position, toInsert);
168
- return [newMetadataProperty];
189
+ return (0, js_1.insertChange)(host, source, filePath, position, toInsert);
169
190
  }
170
191
  const assignment = matchingProperties[0];
171
192
  // If it's not an array, nothing we can do really.
172
- if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
173
- return [];
193
+ if (assignment.initializer.kind !== tsModule.SyntaxKind.ArrayLiteralExpression) {
194
+ return source;
174
195
  }
175
196
  const arrLiteral = assignment.initializer;
176
197
  if (arrLiteral.elements.length == 0) {
@@ -182,20 +203,20 @@ function addSymbolToNgModuleMetadata(source, ngModulePath, metadataField, expres
182
203
  }
183
204
  if (!node) {
184
205
  console.log('No app module found. Please add your new class to your component.');
185
- return [];
206
+ return source;
186
207
  }
187
208
  const isArray = Array.isArray(node);
188
209
  if (isArray) {
189
210
  const nodeArray = node;
190
211
  const symbolsArray = nodeArray.map((node) => node.getText());
191
212
  if (symbolsArray.includes(expression)) {
192
- return [];
213
+ return source;
193
214
  }
194
215
  node = node[node.length - 1];
195
216
  }
196
217
  let toInsert;
197
218
  let position = node.getEnd();
198
- if (!isArray && node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
219
+ if (!isArray && node.kind == tsModule.SyntaxKind.ObjectLiteralExpression) {
199
220
  // We haven't found the field in the metadata declaration. Insert a new
200
221
  // field.
201
222
  const expr = node;
@@ -216,7 +237,8 @@ function addSymbolToNgModuleMetadata(source, ngModulePath, metadataField, expres
216
237
  }
217
238
  }
218
239
  }
219
- else if (!isArray && node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
240
+ else if (!isArray &&
241
+ node.kind == tsModule.SyntaxKind.ArrayLiteralExpression) {
220
242
  // We found the field but it's empty. Insert it just before the `]`.
221
243
  position--;
222
244
  toInsert = `${expression}`;
@@ -231,54 +253,130 @@ function addSymbolToNgModuleMetadata(source, ngModulePath, metadataField, expres
231
253
  toInsert = `, ${expression}`;
232
254
  }
233
255
  }
234
- const insert = new xplat_1.InsertChange(ngModulePath, position, toInsert);
235
- return [insert];
256
+ return (0, js_1.insertChange)(host, source, filePath, position, toInsert);
236
257
  }
237
- exports.addSymbolToNgModuleMetadata = addSymbolToNgModuleMetadata;
238
- function removeFromNgModule(source, modulePath, property) {
258
+ function _addSymbolToNgModuleMetadata(host, source, ngModulePath, metadataField, expression) {
259
+ return _addSymbolToDecoratorMetadata(host, source, ngModulePath, metadataField, expression, 'NgModule');
260
+ }
261
+ exports._addSymbolToNgModuleMetadata = _addSymbolToNgModuleMetadata;
262
+ function removeFromNgModule(host, source, modulePath, property) {
239
263
  const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
240
264
  let node = nodes[0]; // tslint:disable-line:no-any
241
265
  // Find the decorator declaration.
242
266
  if (!node) {
243
- return [];
267
+ return source;
244
268
  }
245
269
  // Get all the children property assignment of object literals.
246
270
  const matchingProperty = getMatchingProperty(source, property, 'NgModule', '@angular/core');
247
271
  if (matchingProperty) {
248
- return [
249
- new xplat_1.RemoveChange(modulePath, matchingProperty.getStart(source), matchingProperty.getFullText(source)),
250
- ];
251
- }
252
- else {
253
- return [];
272
+ return (0, js_1.removeChange)(host, source, modulePath, matchingProperty.getStart(source), matchingProperty.getFullText(source));
254
273
  }
255
274
  }
256
275
  exports.removeFromNgModule = removeFromNgModule;
257
- function addImportToModule(source, modulePath, symbolName) {
258
- return addSymbolToNgModuleMetadata(source, modulePath, 'imports', symbolName);
276
+ /**
277
+ * Add an import to a Standalone Component
278
+ * @param host Virtual Tree
279
+ * @param source TS Source File containing the Component
280
+ * @param componentPath The path to the Component
281
+ * @param symbolName The import to add to the Component
282
+ */
283
+ function addImportToComponent(host, source, componentPath, symbolName) {
284
+ return _addSymbolToDecoratorMetadata(host, source, componentPath, 'imports', symbolName, 'Component');
285
+ }
286
+ exports.addImportToComponent = addImportToComponent;
287
+ /**
288
+ * Add an import to a Standalone Directive
289
+ * @param host Virtual Tree
290
+ * @param source TS Source File containing the Directive
291
+ * @param directivePath The path to the Directive
292
+ * @param symbolName The import to add to the Directive
293
+ */
294
+ function addImportToDirective(host, source, directivePath, symbolName) {
295
+ return _addSymbolToDecoratorMetadata(host, source, directivePath, 'imports', symbolName, 'Directive');
296
+ }
297
+ exports.addImportToDirective = addImportToDirective;
298
+ /**
299
+ * Add an import to a Standalone Pipe
300
+ * @param host Virtual Tree
301
+ * @param source TS Source File containing the Pipe
302
+ * @param pipePath The path to the Pipe
303
+ * @param symbolName The import to add to the Pipe
304
+ */
305
+ function addImportToPipe(host, source, pipePath, symbolName) {
306
+ return _addSymbolToDecoratorMetadata(host, source, pipePath, 'imports', symbolName, 'Pipe');
307
+ }
308
+ exports.addImportToPipe = addImportToPipe;
309
+ /**
310
+ * Add an import to an NgModule
311
+ * @param host Virtual Tree
312
+ * @param source TS Source File containing the NgModule
313
+ * @param modulePath The path to the NgModule
314
+ * @param symbolName The import to add to the NgModule
315
+ */
316
+ function addImportToModule(host, source, modulePath, symbolName) {
317
+ return _addSymbolToNgModuleMetadata(host, source, modulePath, 'imports', symbolName);
259
318
  }
260
319
  exports.addImportToModule = addImportToModule;
261
- function addImportToTestBed(source, specPath, symbolName) {
262
- const allCalls = ((0, typescript_1.findNodes)(source, ts.SyntaxKind.CallExpression));
320
+ function addImportToTestBed(host, source, specPath, symbolName) {
321
+ if (!tsModule) {
322
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
323
+ }
324
+ const allCalls = ((0, typescript_1.findNodes)(source, tsModule.SyntaxKind.CallExpression));
263
325
  const configureTestingModuleObjectLiterals = allCalls
264
- .filter((c) => c.expression.kind === ts.SyntaxKind.PropertyAccessExpression)
326
+ .filter((c) => c.expression.kind === tsModule.SyntaxKind.PropertyAccessExpression)
265
327
  .filter((c) => c.expression.name.getText(source) === 'configureTestingModule')
266
- .map((c) => c.arguments[0].kind === ts.SyntaxKind.ObjectLiteralExpression
328
+ .map((c) => c.arguments[0].kind === tsModule.SyntaxKind.ObjectLiteralExpression
267
329
  ? c.arguments[0]
268
330
  : null);
269
331
  if (configureTestingModuleObjectLiterals.length > 0) {
270
332
  const startPosition = configureTestingModuleObjectLiterals[0]
271
333
  .getFirstToken(source)
272
334
  .getEnd();
273
- return [
274
- new xplat_1.InsertChange(specPath, startPosition, `imports: [${symbolName}], `),
275
- ];
276
- }
277
- else {
278
- return [];
335
+ return (0, js_1.insertChange)(host, source, specPath, startPosition, `imports: [${symbolName}], `);
279
336
  }
337
+ return source;
280
338
  }
281
339
  exports.addImportToTestBed = addImportToTestBed;
340
+ function addDeclarationsToTestBed(host, source, specPath, symbolName) {
341
+ if (!tsModule) {
342
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
343
+ }
344
+ const allCalls = ((0, typescript_1.findNodes)(source, tsModule.SyntaxKind.CallExpression));
345
+ const configureTestingModuleObjectLiterals = allCalls
346
+ .filter((c) => c.expression.kind === tsModule.SyntaxKind.PropertyAccessExpression)
347
+ .filter((c) => c.expression.name.getText(source) === 'configureTestingModule')
348
+ .map((c) => c.arguments[0].kind === tsModule.SyntaxKind.ObjectLiteralExpression
349
+ ? c.arguments[0]
350
+ : null);
351
+ if (configureTestingModuleObjectLiterals.length > 0) {
352
+ const startPosition = configureTestingModuleObjectLiterals[0]
353
+ .getFirstToken(source)
354
+ .getEnd();
355
+ return (0, js_1.insertChange)(host, source, specPath, startPosition, `declarations: [${symbolName.join(',')}], `);
356
+ }
357
+ return source;
358
+ }
359
+ exports.addDeclarationsToTestBed = addDeclarationsToTestBed;
360
+ function replaceIntoToTestBed(host, source, specPath, newSymbol, previousSymbol) {
361
+ if (!tsModule) {
362
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
363
+ }
364
+ const allCalls = ((0, typescript_1.findNodes)(source, tsModule.SyntaxKind.CallExpression));
365
+ const configureTestingModuleObjectLiterals = allCalls
366
+ .filter((c) => c.expression.kind === tsModule.SyntaxKind.PropertyAccessExpression)
367
+ .filter((c) => c.expression.name.getText(source) === 'configureTestingModule')
368
+ .map((c) => c.arguments[0].kind === tsModule.SyntaxKind.ObjectLiteralExpression
369
+ ? c.arguments[0]
370
+ : null);
371
+ if (configureTestingModuleObjectLiterals.length > 0) {
372
+ const startPosition = configureTestingModuleObjectLiterals[0]
373
+ .getFirstToken(source)
374
+ .getEnd();
375
+ return (0, js_1.replaceChange)(host, source, specPath, startPosition, newSymbol, previousSymbol);
376
+ }
377
+ return source;
378
+ }
379
+ exports.replaceIntoToTestBed = replaceIntoToTestBed;
282
380
  function getBootstrapComponent(source, moduleClassName) {
283
381
  const bootstrap = getMatchingProperty(source, 'bootstrap', 'NgModule', '@angular/core');
284
382
  if (!bootstrap) {
@@ -301,56 +399,113 @@ function getMatchingProperty(source, property, identifier, module) {
301
399
  // Get all the children property assignment of object literals.
302
400
  return getMatchingObjectLiteralElement(node, source, property);
303
401
  }
304
- function addRoute(ngModulePath, source, route) {
402
+ function addRouteToNgModule(host, ngModulePath, source, route) {
305
403
  const routes = getListOfRoutes(source);
306
404
  if (!routes)
307
- return [];
405
+ return source;
308
406
  if (routes.hasTrailingComma || routes.length === 0) {
309
- return [new xplat_1.InsertChange(ngModulePath, routes.end, route)];
407
+ return (0, js_1.insertChange)(host, source, ngModulePath, routes.end, route);
310
408
  }
311
409
  else {
312
- return [new xplat_1.InsertChange(ngModulePath, routes.end, `, ${route}`)];
410
+ return (0, js_1.insertChange)(host, source, ngModulePath, routes.end, `, ${route}`);
313
411
  }
314
412
  }
315
- exports.addRoute = addRoute;
413
+ exports.addRouteToNgModule = addRouteToNgModule;
316
414
  function getListOfRoutes(source) {
415
+ if (!tsModule) {
416
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
417
+ }
317
418
  const imports = getMatchingProperty(source, 'imports', 'NgModule', '@angular/core');
318
- if (imports.initializer.kind === ts.SyntaxKind.ArrayLiteralExpression) {
419
+ if ((imports === null || imports === void 0 ? void 0 : imports.initializer.kind) === tsModule.SyntaxKind.ArrayLiteralExpression) {
319
420
  const a = imports.initializer;
320
- for (let e of a.elements) {
321
- if (e.kind === ts.SyntaxKind.CallExpression) {
421
+ for (const e of a.elements) {
422
+ if (e.kind === tsModule.SyntaxKind.CallExpression) {
322
423
  const ee = e;
323
424
  const text = ee.expression.getText(source);
324
425
  if ((text === 'RouterModule.forRoot' ||
325
426
  text === 'RouterModule.forChild') &&
326
427
  ee.arguments.length > 0) {
327
428
  const routes = ee.arguments[0];
328
- if (routes.kind === ts.SyntaxKind.ArrayLiteralExpression) {
429
+ if (routes.kind === tsModule.SyntaxKind.ArrayLiteralExpression) {
329
430
  return routes.elements;
330
431
  }
432
+ else if (routes.kind === tsModule.SyntaxKind.Identifier) {
433
+ // find the array expression
434
+ const variableDeclarations = (0, typescript_1.findNodes)(source, tsModule.SyntaxKind.VariableDeclaration);
435
+ const routesDeclaration = variableDeclarations.find((x) => {
436
+ return x.name.getText() === routes.getText();
437
+ });
438
+ if (routesDeclaration) {
439
+ return routesDeclaration.initializer.elements;
440
+ }
441
+ }
331
442
  }
332
443
  }
333
444
  }
334
445
  }
335
446
  return null;
336
447
  }
337
- function addProviderToModule(source, modulePath, symbolName) {
338
- return addSymbolToNgModuleMetadata(source, modulePath, 'providers', symbolName);
448
+ /**
449
+ * Add a provider to bootstrapApplication call for Standalone Applications
450
+ * @param tree Virtual Tree
451
+ * @param filePath Path to the file containing the bootstrapApplication call
452
+ * @param providerToAdd Provider to add
453
+ */
454
+ function addProviderToBootstrapApplication(tree, filePath, providerToAdd) {
455
+ (0, ensure_typescript_1.ensureTypescript)();
456
+ const { tsquery } = require('@phenomnomnominal/tsquery');
457
+ const PROVIDERS_ARRAY_SELECTOR = 'CallExpression:has(Identifier[name=bootstrapApplication]) ObjectLiteralExpression > PropertyAssignment:has(Identifier[name=providers]) > ArrayLiteralExpression';
458
+ const fileContents = tree.read(filePath, 'utf-8');
459
+ const ast = tsquery.ast(fileContents);
460
+ const providersArrayNodes = tsquery(ast, PROVIDERS_ARRAY_SELECTOR, {
461
+ visitAllChildren: true,
462
+ });
463
+ if (providersArrayNodes.length === 0) {
464
+ throw new Error(`Providers does not exist in the bootstrapApplication call within ${filePath}.`);
465
+ }
466
+ const arrayNode = providersArrayNodes[0];
467
+ const newFileContents = `${fileContents.slice(0, arrayNode.getStart() + 1)}${providerToAdd},${fileContents.slice(arrayNode.getStart() + 1, fileContents.length)}`;
468
+ tree.write(filePath, newFileContents);
469
+ }
470
+ exports.addProviderToBootstrapApplication = addProviderToBootstrapApplication;
471
+ /**
472
+ * Add a provider to an NgModule
473
+ * @param host Virtual Tree
474
+ * @param source TS Source File containing the NgModule
475
+ * @param modulePath Path to the NgModule
476
+ * @param symbolName The provider to add
477
+ */
478
+ function addProviderToModule(host, source, modulePath, symbolName) {
479
+ return _addSymbolToNgModuleMetadata(host, source, modulePath, 'providers', symbolName);
339
480
  }
340
481
  exports.addProviderToModule = addProviderToModule;
341
- function addDeclarationToModule(source, modulePath, symbolName) {
342
- return addSymbolToNgModuleMetadata(source, modulePath, 'declarations', symbolName);
482
+ /**
483
+ * Add a provider to a Standalone Component
484
+ * @param host Virtual Tree
485
+ * @param source TS Source File containing the Component
486
+ * @param componentPath Path to the Component
487
+ * @param symbolName The provider to add
488
+ */
489
+ function addProviderToComponent(host, source, componentPath, symbolName) {
490
+ return _addSymbolToDecoratorMetadata(host, source, componentPath, 'providers', symbolName, 'Component');
491
+ }
492
+ exports.addProviderToComponent = addProviderToComponent;
493
+ function addDeclarationToModule(host, source, modulePath, symbolName) {
494
+ return _addSymbolToNgModuleMetadata(host, source, modulePath, 'declarations', symbolName);
343
495
  }
344
496
  exports.addDeclarationToModule = addDeclarationToModule;
345
- function addEntryComponents(source, modulePath, symbolName) {
346
- return addSymbolToNgModuleMetadata(source, modulePath, 'entryComponents', symbolName);
497
+ function addEntryComponents(host, source, modulePath, symbolName) {
498
+ return _addSymbolToNgModuleMetadata(host, source, modulePath, 'entryComponents', symbolName);
347
499
  }
348
500
  exports.addEntryComponents = addEntryComponents;
349
501
  function readBootstrapInfo(host, app) {
350
- const config = (0, xplat_1.getProjectConfig)(host, app);
502
+ if (!tsModule) {
503
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
504
+ }
505
+ const config = (0, devkit_1.readProjectConfiguration)(host, app);
351
506
  let mainPath;
352
507
  try {
353
- mainPath = config.architect.build.options.main;
508
+ mainPath = config.targets.build.options.main;
354
509
  }
355
510
  catch (e) {
356
511
  throw new Error('Main file cannot be located');
@@ -359,21 +514,21 @@ function readBootstrapInfo(host, app) {
359
514
  throw new Error('Main file cannot be located');
360
515
  }
361
516
  const mainSource = host.read(mainPath).toString('utf-8');
362
- const main = ts.createSourceFile(mainPath, mainSource, ts.ScriptTarget.Latest, true);
363
- const moduleImports = (0, xplat_1.getImport)(main, (s) => s.indexOf('.module') > -1);
517
+ const main = tsModule.createSourceFile(mainPath, mainSource, tsModule.ScriptTarget.Latest, true);
518
+ const moduleImports = (0, js_1.getImport)(main, (s) => s.indexOf('.module') > -1);
364
519
  if (moduleImports.length !== 1) {
365
520
  throw new Error(`main.ts can only import a single module`);
366
521
  }
367
522
  const moduleImport = moduleImports[0];
368
523
  const moduleClassName = moduleImport.bindings.filter((b) => b.endsWith('Module'))[0];
369
- const modulePath = `${path.join(path.dirname(mainPath), moduleImport.moduleSpec)}.ts`;
524
+ const modulePath = `${(0, path_1.join)((0, path_1.dirname)(mainPath), moduleImport.moduleSpec)}.ts`;
370
525
  if (!host.exists(modulePath)) {
371
526
  throw new Error(`Cannot find '${modulePath}'`);
372
527
  }
373
528
  const moduleSourceText = host.read(modulePath).toString('utf-8');
374
- const moduleSource = ts.createSourceFile(modulePath, moduleSourceText, ts.ScriptTarget.Latest, true);
529
+ const moduleSource = tsModule.createSourceFile(modulePath, moduleSourceText, tsModule.ScriptTarget.Latest, true);
375
530
  const bootstrapComponentClassName = getBootstrapComponent(moduleSource, moduleClassName);
376
- const bootstrapComponentFileName = `./${path.join(path.dirname(moduleImport.moduleSpec), `${(0, xplat_utils_1.toFileName)(bootstrapComponentClassName.substring(0, bootstrapComponentClassName.length - 9))}.component`)}`;
531
+ const bootstrapComponentFileName = `./${(0, path_1.join)((0, path_1.dirname)(moduleImport.moduleSpec), `${(0, devkit_1.names)(bootstrapComponentClassName.substring(0, bootstrapComponentClassName.length - 9)).fileName}.component`)}`;
377
532
  return {
378
533
  moduleSpec: moduleImport.moduleSpec,
379
534
  mainPath,
@@ -386,25 +541,44 @@ function readBootstrapInfo(host, app) {
386
541
  }
387
542
  exports.readBootstrapInfo = readBootstrapInfo;
388
543
  function getDecoratorPropertyValueNode(host, modulePath, identifier, property, module) {
544
+ if (!tsModule) {
545
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
546
+ }
389
547
  const moduleSourceText = host.read(modulePath).toString('utf-8');
390
- const moduleSource = ts.createSourceFile(modulePath, moduleSourceText, ts.ScriptTarget.Latest, true);
548
+ const moduleSource = tsModule.createSourceFile(modulePath, moduleSourceText, tsModule.ScriptTarget.Latest, true);
391
549
  const templateNode = getMatchingProperty(moduleSource, property, identifier, module);
392
550
  return templateNode.getChildAt(templateNode.getChildCount() - 1);
393
551
  }
394
552
  exports.getDecoratorPropertyValueNode = getDecoratorPropertyValueNode;
395
553
  function getMatchingObjectLiteralElement(node, source, property) {
554
+ if (!tsModule) {
555
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
556
+ }
396
557
  return (node.properties
397
- .filter((prop) => prop.kind == ts.SyntaxKind.PropertyAssignment)
558
+ .filter((prop) => prop.kind == tsModule.SyntaxKind.PropertyAssignment)
398
559
  // Filter out every fields that's not "metadataField". Also handles string literals
399
560
  // (but not expressions).
400
561
  .filter((prop) => {
401
562
  const name = prop.name;
402
563
  switch (name.kind) {
403
- case ts.SyntaxKind.Identifier:
564
+ case tsModule.SyntaxKind.Identifier:
404
565
  return name.getText(source) === property;
405
- case ts.SyntaxKind.StringLiteral:
566
+ case tsModule.SyntaxKind.StringLiteral:
406
567
  return name.text === property;
407
568
  }
408
569
  return false;
409
570
  })[0]);
410
571
  }
572
+ function getTsSourceFile(host, path) {
573
+ if (!tsModule) {
574
+ tsModule = (0, ensure_typescript_1.ensureTypescript)();
575
+ }
576
+ const buffer = host.read(path);
577
+ if (!buffer) {
578
+ throw new Error(`Could not read TS file (${path}).`);
579
+ }
580
+ const content = buffer.toString();
581
+ const source = tsModule.createSourceFile(path, content, tsModule.ScriptTarget.Latest, true);
582
+ return source;
583
+ }
584
+ exports.getTsSourceFile = getTsSourceFile;