@lukfel/ng-scaffold 20.0.48 → 20.0.50
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/package.json +5 -5
- package/schematics/collection.json +2 -2
- package/schematics/ng-add/add-styles.d.ts +2 -0
- package/schematics/ng-add/{add-styles.ts → add-styles.js} +33 -35
- package/schematics/ng-add/add-styles.js.map +1 -0
- package/schematics/ng-add/index.d.ts +2 -0
- package/schematics/ng-add/index.js +17 -0
- package/schematics/ng-add/index.js.map +1 -0
- package/schematics/ng-add/add-module.ts +0 -74
- package/schematics/ng-add/add-service.ts +0 -117
- package/schematics/ng-add/add-template.ts +0 -107
- package/schematics/ng-add/index.ts +0 -14
- package/schematics/ng-add/ts-ast-utils.ts +0 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukfel/ng-scaffold",
|
|
3
|
-
"version": "20.0.
|
|
3
|
+
"version": "20.0.50",
|
|
4
4
|
"description": "This Angular library provides a basic UI scaffold and services for modern web and mobile apps",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -16,9 +16,6 @@
|
|
|
16
16
|
"@angular/core": "^20.0.5",
|
|
17
17
|
"@angular/material": "^20.0.5"
|
|
18
18
|
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"tslib": "^2.3.0"
|
|
21
|
-
},
|
|
22
19
|
"exports": {
|
|
23
20
|
"./styles": {
|
|
24
21
|
"sass": "./styles/style.scss"
|
|
@@ -37,5 +34,8 @@
|
|
|
37
34
|
},
|
|
38
35
|
"sideEffects": false,
|
|
39
36
|
"module": "fesm2022/lukfel-ng-scaffold.mjs",
|
|
40
|
-
"typings": "index.d.ts"
|
|
37
|
+
"typings": "index.d.ts",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"tslib": "^2.3.0"
|
|
40
|
+
}
|
|
41
41
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
|
|
3
3
|
"schematics": {
|
|
4
4
|
"ng-add": {
|
|
5
|
-
"description": "Add ng-scaffold to the project",
|
|
5
|
+
"description": "Add ng-scaffold to the project.",
|
|
6
6
|
"factory": "./ng-add/index#ngAdd",
|
|
7
7
|
"schema": "./ng-add/schema.json"
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
}
|
|
10
|
+
}
|
|
@@ -1,40 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addStyles = addStyles;
|
|
3
4
|
const SNIPPET = `
|
|
4
5
|
@use "@lukfel/ng-scaffold/styles" as lf;
|
|
5
6
|
// Create a base theme and include it with @include lf.scaffold-theme($base-theme);
|
|
6
7
|
// Include additional themes with @include lf.scaffold-colors($theme2, 'theme2');
|
|
7
8
|
|
|
8
|
-
`;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'src/styles.
|
|
16
|
-
'src/styles.
|
|
17
|
-
'
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return tree;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
9
|
+
`;
|
|
10
|
+
function addStyles() {
|
|
11
|
+
return (tree, context) => {
|
|
12
|
+
context.logger.info('[Styles] Searching for styles file ...');
|
|
13
|
+
const possiblePaths = [
|
|
14
|
+
'src/styles.scss',
|
|
15
|
+
'src/styles.sass',
|
|
16
|
+
'src/styles.css',
|
|
17
|
+
'src/styles.css.scss',
|
|
18
|
+
'styles.scss'
|
|
19
|
+
];
|
|
20
|
+
const path = possiblePaths.find(p => tree.exists(p));
|
|
21
|
+
if (!path) {
|
|
22
|
+
context.logger.warn('[Styles] No global styles file found. Skip.');
|
|
23
|
+
return tree;
|
|
24
|
+
}
|
|
25
|
+
;
|
|
26
|
+
const content = tree.read(path).toString('utf-8');
|
|
27
|
+
if (content.includes('@lukfel/ng-scaffold/styles') || content.includes('lf.scaffold-theme')) {
|
|
28
|
+
context.logger.info('[Styles] Styles already added. Skip.');
|
|
29
|
+
return tree;
|
|
30
|
+
}
|
|
31
|
+
const recorder = tree.beginUpdate(path);
|
|
32
|
+
recorder.insertLeft(content.length, SNIPPET);
|
|
33
|
+
tree.commitUpdate(recorder);
|
|
34
|
+
context.logger.info('[Styles] Successfully added.');
|
|
35
|
+
return tree;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=add-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-styles.js","sourceRoot":"","sources":["../../../../projects/ng-scaffold/schematics/ng-add/add-styles.ts"],"names":[],"mappings":";;AASA,8BA8BC;AArCD,MAAM,OAAO,GAAG;;;;;CAKf,CAAC;AAEF,SAAgB,SAAS;IACrB,OAAO,CAAC,IAAU,EAAE,OAAyB,EAAE,EAAE;QAC7C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QAE9D,MAAM,aAAa,GAAG;YAClB,iBAAiB;YACjB,iBAAiB;YACjB,gBAAgB;YAChB,qBAAqB;YACrB,aAAa;SAChB,CAAC;QAEF,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;YACnE,OAAO,IAAI,CAAA;QACf,CAAC;QAAA,CAAC;QAEF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC1F,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YAC5D,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC;IAChB,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = ngAdd;
|
|
4
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
|
+
// import { addModule } from './add-module';
|
|
6
|
+
// import { addService } from './add-service';
|
|
7
|
+
const add_styles_1 = require("./add-styles");
|
|
8
|
+
// import { addTemplate } from './add-template';
|
|
9
|
+
function ngAdd() {
|
|
10
|
+
return (0, schematics_1.chain)([
|
|
11
|
+
// addModule(),
|
|
12
|
+
// addService(),
|
|
13
|
+
// addTemplate(),
|
|
14
|
+
(0, add_styles_1.addStyles)(),
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ng-scaffold/schematics/ng-add/index.ts"],"names":[],"mappings":";;AAMA,wBAOC;AAbD,2DAAyD;AACzD,4CAA4C;AAC5C,8CAA8C;AAC9C,6CAAyC;AACzC,gDAAgD;AAEhD,SAAwB,KAAK;IACzB,OAAO,IAAA,kBAAK,EAAC;QACT,eAAe;QACf,gBAAgB;QAChB,iBAAiB;QACjB,IAAA,sBAAS,GAAE;KACd,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
|
|
2
|
-
import * as ts from 'typescript';
|
|
3
|
-
import { getTsSourceFile } from './ts-ast-utils';
|
|
4
|
-
|
|
5
|
-
export function addModule(): Rule {
|
|
6
|
-
return (tree: Tree) => {
|
|
7
|
-
const mainPaths = ['src/main.ts', 'main.ts'];
|
|
8
|
-
let mainPath: string | undefined;
|
|
9
|
-
|
|
10
|
-
for (const p of mainPaths) {
|
|
11
|
-
if (tree.exists(p)) {
|
|
12
|
-
mainPath = p;
|
|
13
|
-
break;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// --- 1) Standalone bootstrap ---
|
|
18
|
-
if (mainPath) {
|
|
19
|
-
const sourceFile = getTsSourceFile(tree, mainPath);
|
|
20
|
-
if (!sourceFile) throw new SchematicsException(`Could not read ${mainPath}`);
|
|
21
|
-
const content = tree.read(mainPath)!.toString('utf-8');
|
|
22
|
-
|
|
23
|
-
if (!content.includes('@lukfel/ng-scaffold')) {
|
|
24
|
-
const importStatements = sourceFile.statements.filter(s => ts.isImportDeclaration(s));
|
|
25
|
-
const insertPos = importStatements.length ? importStatements[importStatements.length - 1].end + 1 : 0;
|
|
26
|
-
const recorder = tree.beginUpdate(mainPath);
|
|
27
|
-
recorder.insertLeft(insertPos, 'import { ScaffoldModule } from \'@lukfel/ng-scaffold\';\n');
|
|
28
|
-
tree.commitUpdate(recorder);
|
|
29
|
-
}
|
|
30
|
-
return tree;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// --- 2) NgModule ---
|
|
34
|
-
const modulePaths = ['src/app/app.module.ts', 'app.module.ts'];
|
|
35
|
-
for (const mp of modulePaths) {
|
|
36
|
-
if (!tree.exists(mp)) continue;
|
|
37
|
-
const sourceFile = getTsSourceFile(tree, mp);
|
|
38
|
-
if (!sourceFile) continue;
|
|
39
|
-
let content = tree.read(mp)!.toString('utf-8');
|
|
40
|
-
|
|
41
|
-
// Add import if missing
|
|
42
|
-
if (!content.includes('@lukfel/ng-scaffold')) {
|
|
43
|
-
const importStatements = sourceFile.statements.filter(s => ts.isImportDeclaration(s));
|
|
44
|
-
const insertPos = importStatements.length ? importStatements[importStatements.length - 1].end + 1 : 0;
|
|
45
|
-
const recorder = tree.beginUpdate(mp);
|
|
46
|
-
recorder.insertLeft(insertPos, 'import { ScaffoldModule } from \'@lukfel/ng-scaffold\';\n');
|
|
47
|
-
tree.commitUpdate(recorder);
|
|
48
|
-
content = tree.read(mp)!.toString('utf-8');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Add ScaffoldModule to imports array
|
|
52
|
-
const importsMatch = /imports\s*:\s*\[([\s\S]*?)\]/m.exec(content);
|
|
53
|
-
if (importsMatch) {
|
|
54
|
-
const arrayStart = content.indexOf('[', importsMatch.index) + 1;
|
|
55
|
-
const recorder = tree.beginUpdate(mp);
|
|
56
|
-
recorder.insertLeft(arrayStart, ' ScaffoldModule,');
|
|
57
|
-
tree.commitUpdate(recorder);
|
|
58
|
-
return tree;
|
|
59
|
-
}
|
|
60
|
-
// No imports array: insert one
|
|
61
|
-
const ngModuleMatch = /@NgModule\s*\(\s*\{/.exec(content);
|
|
62
|
-
if (ngModuleMatch) {
|
|
63
|
-
const insertPos = ngModuleMatch.index + ngModuleMatch[0].length;
|
|
64
|
-
const recorder = tree.beginUpdate(mp);
|
|
65
|
-
recorder.insertLeft(insertPos, '\n imports: [ScaffoldModule],');
|
|
66
|
-
tree.commitUpdate(recorder);
|
|
67
|
-
return tree;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
throw new SchematicsException('Could not find main.ts or app.module.ts to add ScaffoldModule');
|
|
73
|
-
};
|
|
74
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
|
|
2
|
-
import * as ts from 'typescript';
|
|
3
|
-
|
|
4
|
-
function detectBootstrapComponent(tree: Tree): { className: string, filePath: string } | null {
|
|
5
|
-
// Reuse the same detection logic as addTemplate
|
|
6
|
-
const mainPaths = ['src/main.ts', 'main.ts'];
|
|
7
|
-
for (const mp of mainPaths) {
|
|
8
|
-
if (!tree.exists(mp)) continue;
|
|
9
|
-
const text = tree.read(mp)!.toString('utf-8');
|
|
10
|
-
// eslint-disable-next-line no-useless-escape
|
|
11
|
-
const match = /bootstrapApplication\(([^,\)]+)/.exec(text);
|
|
12
|
-
if (match) {
|
|
13
|
-
const className = match[1].trim();
|
|
14
|
-
const importMatch = new RegExp(`import\\s+\\{?\\s*${className}\\s*\\}?\\s+from\\s+['"](.*)['"]`).exec(text);
|
|
15
|
-
if (importMatch) {
|
|
16
|
-
const importPath = importMatch[1].startsWith('.') ? importMatch[1] : `./${importMatch[1]}`;
|
|
17
|
-
const filePath = `src/${importPath.replace(/^\.\/+/, '')}.ts`;
|
|
18
|
-
if (tree.exists(filePath)) return { className, filePath };
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const modulePaths = ['src/app/app.module.ts', 'app.module.ts'];
|
|
24
|
-
for (const mp of modulePaths) {
|
|
25
|
-
if (!tree.exists(mp)) continue;
|
|
26
|
-
const text = tree.read(mp)!.toString('utf-8');
|
|
27
|
-
const match = /bootstrap\s*:\s*\[([^\]]+)\]/.exec(text);
|
|
28
|
-
if (match) {
|
|
29
|
-
const className = match[1].split(',')[0].trim();
|
|
30
|
-
const importMatch = new RegExp(`import\\s+\\{?\\s*${className}\\s*\\}?\\s+from\\s+['"](.*)['"]`).exec(text);
|
|
31
|
-
if (importMatch) {
|
|
32
|
-
const importPath = importMatch[1].startsWith('.') ? importMatch[1] : `./${importMatch[1]}`;
|
|
33
|
-
const filePath = `src/${importPath.replace(/^\.\/+/, '')}.ts`;
|
|
34
|
-
if (tree.exists(filePath)) return { className, filePath };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function addService(): Rule {
|
|
43
|
-
return (tree: Tree) => {
|
|
44
|
-
const detected = detectBootstrapComponent(tree);
|
|
45
|
-
if (!detected) throw new SchematicsException('Could not detect bootstrap component');
|
|
46
|
-
|
|
47
|
-
const { className, filePath } = detected;
|
|
48
|
-
if (!tree.exists(filePath)) throw new SchematicsException(`Component file not found: ${filePath}`);
|
|
49
|
-
|
|
50
|
-
let content = tree.read(filePath)!.toString('utf-8');
|
|
51
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
52
|
-
const recorder = tree.beginUpdate(filePath);
|
|
53
|
-
|
|
54
|
-
// Add imports if missing
|
|
55
|
-
if (!content.includes('@lukfel/ng-scaffold')) {
|
|
56
|
-
content = 'import { ScaffoldService, ScaffoldConfig } from \'@lukfel/ng-scaffold\';\n' + content;
|
|
57
|
-
}
|
|
58
|
-
if (!content.includes('inject')) {
|
|
59
|
-
content = 'import { inject } from \'@angular/core\';\n' + content;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Find class node
|
|
63
|
-
let classNode: ts.ClassDeclaration | undefined;
|
|
64
|
-
function visit(node: ts.Node): void {
|
|
65
|
-
if (ts.isClassDeclaration(node) && node.name?.text === className) {
|
|
66
|
-
classNode = node;
|
|
67
|
-
} else ts.forEachChild(node, visit);
|
|
68
|
-
}
|
|
69
|
-
visit(sourceFile);
|
|
70
|
-
|
|
71
|
-
if (!classNode) throw new SchematicsException(`Could not find class ${className} in ${filePath}`);
|
|
72
|
-
|
|
73
|
-
// Determine constructor
|
|
74
|
-
const constructorNode = classNode.members.find(ts.isConstructorDeclaration);
|
|
75
|
-
|
|
76
|
-
if (constructorNode) {
|
|
77
|
-
// Existing constructor: inject via parameter if missing
|
|
78
|
-
const paramExists = constructorNode.parameters.some(p =>
|
|
79
|
-
p.type?.getText(sourceFile).includes('ScaffoldService')
|
|
80
|
-
);
|
|
81
|
-
if (!paramExists) {
|
|
82
|
-
const ctorStart = constructorNode.getStart(sourceFile);
|
|
83
|
-
const ctorText = constructorNode.getFullText(sourceFile);
|
|
84
|
-
const insertPos = ctorStart + ctorText.indexOf('(') + 1;
|
|
85
|
-
recorder.insertLeft(insertPos, 'private scaffoldService: ScaffoldService, ');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Add assignment inside constructor body if missing
|
|
89
|
-
const bodyStart = constructorNode.body!.getStart(sourceFile);
|
|
90
|
-
const bodyEnd = constructorNode.body!.getEnd();
|
|
91
|
-
const bodyText = content.slice(bodyStart, bodyEnd);
|
|
92
|
-
if (!bodyText.includes('this.scaffoldService.scaffoldConfig')) {
|
|
93
|
-
recorder.insertLeft(bodyEnd - 1, '\n this.scaffoldService.scaffoldConfig = this.scaffoldConfig;\n');
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
// No constructor: use inject() + new constructor
|
|
97
|
-
const classStart = classNode.getStart(sourceFile);
|
|
98
|
-
const classHeaderEnd = content.indexOf('{', classStart) + 1;
|
|
99
|
-
|
|
100
|
-
const snippet = `
|
|
101
|
-
private scaffoldService = inject(ScaffoldService);
|
|
102
|
-
private scaffoldConfig: ScaffoldConfig = {
|
|
103
|
-
// Create your own config or generate it at https://lukfel.github.io/ng-scaffold
|
|
104
|
-
};
|
|
105
|
-
constructor() {
|
|
106
|
-
this.scaffoldService.scaffoldConfig = this.scaffoldConfig;
|
|
107
|
-
}
|
|
108
|
-
`;
|
|
109
|
-
|
|
110
|
-
recorder.insertLeft(classHeaderEnd, snippet);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
tree.commitUpdate(recorder);
|
|
114
|
-
return tree;
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
|
|
2
|
-
import * as ts from 'typescript';
|
|
3
|
-
|
|
4
|
-
function detectBootstrapComponent(tree: Tree): { className: string, filePath: string } | null {
|
|
5
|
-
// simplified detection: main.ts (standalone) or AppModule
|
|
6
|
-
const mainPaths = ['src/main.ts', 'main.ts'];
|
|
7
|
-
for (const mp of mainPaths) {
|
|
8
|
-
if (!tree.exists(mp)) continue;
|
|
9
|
-
const text = tree.read(mp)!.toString('utf-8');
|
|
10
|
-
// eslint-disable-next-line no-useless-escape
|
|
11
|
-
const match = /bootstrapApplication\(([^,\)]+)/.exec(text);
|
|
12
|
-
if (match) {
|
|
13
|
-
const className = match[1].trim();
|
|
14
|
-
const importMatch = new RegExp(`import\\s+\\{?\\s*${className}\\s*\\}?\\s+from\\s+['"](.*)['"]`).exec(text);
|
|
15
|
-
if (importMatch) {
|
|
16
|
-
const importPath = importMatch[1].startsWith('.') ? importMatch[1] : `./${importMatch[1]}`;
|
|
17
|
-
const filePath = `src/${importPath.replace(/^\.\/+/, '')}.ts`;
|
|
18
|
-
if (tree.exists(filePath)) return { className, filePath };
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const modulePaths = ['src/app/app.module.ts', 'app.module.ts'];
|
|
24
|
-
for (const mp of modulePaths) {
|
|
25
|
-
if (!tree.exists(mp)) continue;
|
|
26
|
-
const text = tree.read(mp)!.toString('utf-8');
|
|
27
|
-
const match = /bootstrap\s*:\s*\[([^\]]+)\]/.exec(text);
|
|
28
|
-
if (match) {
|
|
29
|
-
const className = match[1].split(',')[0].trim();
|
|
30
|
-
const importMatch = new RegExp(`import\\s+\\{?\\s*${className}\\s*\\}?\\s+from\\s+['"](.*)['"]`).exec(text);
|
|
31
|
-
if (importMatch) {
|
|
32
|
-
const importPath = importMatch[1].startsWith('.') ? importMatch[1] : `./${importMatch[1]}`;
|
|
33
|
-
const filePath = `src/${importPath.replace(/^\.\/+/, '')}.ts`;
|
|
34
|
-
if (tree.exists(filePath)) return { className, filePath };
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function addTemplate(): Rule {
|
|
43
|
-
return (tree: Tree) => {
|
|
44
|
-
const detected = detectBootstrapComponent(tree);
|
|
45
|
-
if (!detected) throw new SchematicsException('Could not detect bootstrap component');
|
|
46
|
-
|
|
47
|
-
const { className, filePath } = detected;
|
|
48
|
-
if (!tree.exists(filePath)) throw new SchematicsException(`Component file not found: ${filePath}`);
|
|
49
|
-
|
|
50
|
-
const content = tree.read(filePath)!.toString('utf-8');
|
|
51
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
52
|
-
const recorder = tree.beginUpdate(filePath);
|
|
53
|
-
let modified = false;
|
|
54
|
-
|
|
55
|
-
function visit(node: ts.Node): void {
|
|
56
|
-
if (ts.isClassDeclaration(node) && node.name?.text === className) {
|
|
57
|
-
// ✅ Correct type assertion for decorators
|
|
58
|
-
const decorators = (node as ts.ClassDeclaration & { decorators?: ts.NodeArray<ts.Decorator> }).decorators;
|
|
59
|
-
if (!decorators) return;
|
|
60
|
-
|
|
61
|
-
const componentDecorator = decorators.find(d => d.getText(sourceFile).startsWith('@Component'));
|
|
62
|
-
if (!componentDecorator) return;
|
|
63
|
-
|
|
64
|
-
const decoratorText = componentDecorator.getFullText(sourceFile);
|
|
65
|
-
|
|
66
|
-
// templateUrl
|
|
67
|
-
const templateUrlMatch = /templateUrl\s*:\s*['"`](.*?)['"`]/.exec(decoratorText);
|
|
68
|
-
if (templateUrlMatch) {
|
|
69
|
-
const templatePathRaw = templateUrlMatch[1];
|
|
70
|
-
// eslint-disable-next-line no-useless-escape
|
|
71
|
-
const compDir = filePath.replace(/\/[^\/]+$/, '');
|
|
72
|
-
let templatePath = templatePathRaw.startsWith('./')
|
|
73
|
-
? `${compDir}/${templatePathRaw.slice(2)}`
|
|
74
|
-
: `${compDir}/${templatePathRaw}`;
|
|
75
|
-
if (!tree.exists(templatePath)) templatePath += '.html';
|
|
76
|
-
if (!tree.exists(templatePath)) throw new SchematicsException(`Template not found: ${templatePath}`);
|
|
77
|
-
const html = tree.read(templatePath)!.toString('utf-8');
|
|
78
|
-
if (!html.includes('<lf-scaffold>')) {
|
|
79
|
-
recorder.remove(0, html.length);
|
|
80
|
-
recorder.insertLeft(0, `<lf-scaffold>\n${html}\n</lf-scaffold>`);
|
|
81
|
-
modified = true;
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
// inline template
|
|
85
|
-
const templateMatch = /template\s*:\s*(`([\s\S]*?)`|['"`]([\s\S]*?)['"`])/.exec(decoratorText);
|
|
86
|
-
if (templateMatch) {
|
|
87
|
-
const inner = templateMatch[2] ?? templateMatch[3] ?? '';
|
|
88
|
-
const start = content.indexOf(inner);
|
|
89
|
-
if (start !== -1) {
|
|
90
|
-
recorder.remove(start, inner.length);
|
|
91
|
-
recorder.insertLeft(start, `<lf-scaffold>\n${inner}\n</lf-scaffold>`);
|
|
92
|
-
modified = true;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
ts.forEachChild(node, visit);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
visit(sourceFile);
|
|
102
|
-
|
|
103
|
-
if (modified) tree.commitUpdate(recorder);
|
|
104
|
-
|
|
105
|
-
return tree;
|
|
106
|
-
};
|
|
107
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { chain, Rule } from '@angular-devkit/schematics';
|
|
2
|
-
// import { addModule } from './add-module';
|
|
3
|
-
// import { addService } from './add-service';
|
|
4
|
-
import { addStyles } from './add-styles';
|
|
5
|
-
// import { addTemplate } from './add-template';
|
|
6
|
-
|
|
7
|
-
export default function ngAdd(): Rule {
|
|
8
|
-
return chain([
|
|
9
|
-
// addModule(),
|
|
10
|
-
// addService(),
|
|
11
|
-
// addTemplate(),
|
|
12
|
-
addStyles(),
|
|
13
|
-
]);
|
|
14
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Tree } from '@angular-devkit/schematics';
|
|
2
|
-
import { InsertChange, ReplaceChange } from '@schematics/angular/utility/change';
|
|
3
|
-
import * as ts from 'typescript';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Read a file into a ts.SourceFile
|
|
7
|
-
*/
|
|
8
|
-
export function getTsSourceFile(tree: Tree, path: string): ts.SourceFile | null {
|
|
9
|
-
const buffer = tree.read(path);
|
|
10
|
-
if (!buffer) return null;
|
|
11
|
-
const content = buffer.toString('utf-8');
|
|
12
|
-
return ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Create InsertChange or ReplaceChange compatible object
|
|
17
|
-
*/
|
|
18
|
-
export function applyInsertChanges(tree: Tree, filePath: string, changes: Array<InsertChange | ReplaceChange>): void {
|
|
19
|
-
const recorder = tree.beginUpdate(filePath);
|
|
20
|
-
for (const change of changes) {
|
|
21
|
-
// InsertChange has 'pos' and 'toAdd'
|
|
22
|
-
if ((change as any).pos !== undefined && (change as any).toAdd !== undefined) {
|
|
23
|
-
recorder.insertLeft((change as any).pos, (change as any).toAdd);
|
|
24
|
-
} else if ((change as any).order != undefined) {
|
|
25
|
-
// ignore other change shapes
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
tree.commitUpdate(recorder);
|
|
29
|
-
}
|