@silvestv/migration-planificator 3.0.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.
Files changed (122) hide show
  1. package/LICENSE +96 -0
  2. package/README.fr.md +359 -0
  3. package/README.md +360 -0
  4. package/SECURITY.md +187 -0
  5. package/dist/client.bundle.js +357 -0
  6. package/dist/src/core/app-analyzer.js +134 -0
  7. package/dist/src/core/ast/matchers/html/html-attribute-matcher.js +86 -0
  8. package/dist/src/core/ast/matchers/html/html-component-matcher.js +40 -0
  9. package/dist/src/core/ast/matchers/html/html-element-matcher.js +54 -0
  10. package/dist/src/core/ast/matchers/html/html-parser.js +58 -0
  11. package/dist/src/core/ast/matchers/html/html-pipe-matcher.js +95 -0
  12. package/dist/src/core/ast/matchers/html/html-text-matcher.js +53 -0
  13. package/dist/src/core/ast/matchers/html/index.js +118 -0
  14. package/dist/src/core/ast/matchers/index.js +377 -0
  15. package/dist/src/core/ast/matchers/ts/collection-matcher.js +51 -0
  16. package/dist/src/core/ast/matchers/ts/context-matcher.js +275 -0
  17. package/dist/src/core/ast/matchers/ts/decorator-matcher.js +465 -0
  18. package/dist/src/core/ast/matchers/ts/expression-matcher.js +237 -0
  19. package/dist/src/core/ast/matchers/ts/file-matcher.js +97 -0
  20. package/dist/src/core/ast/matchers/ts/hierarchy-matcher.js +172 -0
  21. package/dist/src/core/ast/matchers/ts/import-matcher.js +39 -0
  22. package/dist/src/core/ast/matchers/ts/index.js +53 -0
  23. package/dist/src/core/ast/matchers/ts/node-matcher.js +156 -0
  24. package/dist/src/core/ast/matchers/ts/symbol-matcher.js +281 -0
  25. package/dist/src/core/ast/matchers/ts/type-matcher.js +207 -0
  26. package/dist/src/core/ast/matchers/utils/matcher-helpers.js +37 -0
  27. package/dist/src/core/ast/scanner-ast.js +444 -0
  28. package/dist/src/core/project-detector.js +196 -0
  29. package/dist/src/core/project-strategy/index.js +9 -0
  30. package/dist/src/core/project-strategy/nx-strategy.js +130 -0
  31. package/dist/src/core/project-strategy/project-strategy.interface.js +2 -0
  32. package/dist/src/core/project-strategy/standalone-strategy.js +74 -0
  33. package/dist/src/core/project-strategy/strategy-factory.js +15 -0
  34. package/dist/src/core/rules-loader.js +89 -0
  35. package/dist/src/core/scan-reporter.js +316 -0
  36. package/dist/src/core/scanner-delta.js +339 -0
  37. package/dist/src/core/scanner-orchestrator.js +266 -0
  38. package/dist/src/core/scanner-regex.js +298 -0
  39. package/dist/src/core/workload/calculator.js +82 -0
  40. package/dist/src/core/workload/constants.js +15 -0
  41. package/dist/src/core/workload/grouping.js +18 -0
  42. package/dist/src/core/workload/hierarchy-calculator.js +127 -0
  43. package/dist/src/core/workload/index.js +11 -0
  44. package/dist/src/core/workload/metadata.js +20 -0
  45. package/dist/src/core/workload/special-workload.js +101 -0
  46. package/dist/src/core/workload/target-resolver.js +34 -0
  47. package/dist/src/data/angular-migration-rules.json +2337 -0
  48. package/dist/src/data/markdown/angular-migration-17-18.md +408 -0
  49. package/dist/src/data/markdown/angular-migration-18-19.md +600 -0
  50. package/dist/src/data/markdown/angular-migration-19-20.md +521 -0
  51. package/dist/src/data/rules/rearchitecture/rearchitecture-rules.json +66 -0
  52. package/dist/src/data/rules/to18/rules-18-obligatoire.json +374 -0
  53. package/dist/src/data/rules/to18/rules-18-optionnelle.json +188 -0
  54. package/dist/src/data/rules/to18/rules-18-recommande.json +218 -0
  55. package/dist/src/data/rules/to19/rules-19-obligatoire.json +348 -0
  56. package/dist/src/data/rules/to19/rules-19-optionnelle.json +223 -0
  57. package/dist/src/data/rules/to19/rules-19-recommande.json +200 -0
  58. package/dist/src/data/rules/to20/rules-20-obligatoire.json +556 -0
  59. package/dist/src/data/rules/to20/rules-20-optionnelle.json +190 -0
  60. package/dist/src/data/rules/to20/rules-20-recommande.json +151 -0
  61. package/dist/src/index.js +161 -0
  62. package/dist/src/models/chip-config.js +45 -0
  63. package/dist/src/models/interfaces/app-details.interface.js +2 -0
  64. package/dist/src/models/interfaces/ast-interfaces.js +5 -0
  65. package/dist/src/models/interfaces/ast-pattern.interface.js +2 -0
  66. package/dist/src/models/interfaces/client-interfaces.js +6 -0
  67. package/dist/src/models/interfaces/detection-stats.interface.js +2 -0
  68. package/dist/src/models/interfaces/html-match.interface.js +2 -0
  69. package/dist/src/models/interfaces/html-report-data.interface.js +2 -0
  70. package/dist/src/models/interfaces/lib-details.interface.js +2 -0
  71. package/dist/src/models/interfaces/migration-rules.interface.js +2 -0
  72. package/dist/src/models/interfaces/parsed-args.interface.js +2 -0
  73. package/dist/src/models/interfaces/project-info.interface.js +2 -0
  74. package/dist/src/models/interfaces/project-overview-data.interface.js +2 -0
  75. package/dist/src/models/interfaces/rule-match.interface.js +2 -0
  76. package/dist/src/models/interfaces/rule.interface.js +2 -0
  77. package/dist/src/models/interfaces/rules-by-priority.interface.js +2 -0
  78. package/dist/src/models/interfaces/scanner-comparison.interface.js +2 -0
  79. package/dist/src/models/interfaces/special-workload.interface.js +2 -0
  80. package/dist/src/models/interfaces/workload-report.interface.js +2 -0
  81. package/dist/src/models/types/build-block-blob.type.js +2 -0
  82. package/dist/src/models/types/migration-version.type.js +2 -0
  83. package/dist/src/models/types/project-type.type.js +2 -0
  84. package/dist/src/models/types/risk-level.type.js +2 -0
  85. package/dist/src/models/types/rule-category.type.js +2 -0
  86. package/dist/src/models/types/rule-priority.type.js +2 -0
  87. package/dist/src/models/types/rule-workload-type.type.js +2 -0
  88. package/dist/src/templates/landing/applications-analyzed.template.js +18 -0
  89. package/dist/src/templates/landing/card-app-info.template.js +63 -0
  90. package/dist/src/templates/landing/card-lib-info.template.js +67 -0
  91. package/dist/src/templates/landing/libs-analyzed.template.js +22 -0
  92. package/dist/src/templates/landing/nx-summary.template.js +115 -0
  93. package/dist/src/templates/landing/project-overview.template.js +27 -0
  94. package/dist/src/templates/page/index-page.template.js +95 -0
  95. package/dist/src/templates/page/main.template.js +83 -0
  96. package/dist/src/templates/page/migration-guide.template.js +175 -0
  97. package/dist/src/templates/page/workload-report.template.js +53 -0
  98. package/dist/src/templates/workload/dashboard.template.js +184 -0
  99. package/dist/src/templates/workload/filters-panel.template.js +215 -0
  100. package/dist/src/templates/workload/guide-rule-card.template.js +107 -0
  101. package/dist/src/templates/workload/hierarchy-nx.template.js +104 -0
  102. package/dist/src/templates/workload/hierarchy-shared.js +163 -0
  103. package/dist/src/templates/workload/hierarchy-standalone.template.js +36 -0
  104. package/dist/src/templates/workload/hierarchy.template.js +35 -0
  105. package/dist/src/templates/workload/rule-modal.template.js +280 -0
  106. package/dist/src/utils/core/args-parser.js +123 -0
  107. package/dist/src/utils/core/array-helpers.js +18 -0
  108. package/dist/src/utils/core/ast-helpers.js +99 -0
  109. package/dist/src/utils/core/file-helpers.js +109 -0
  110. package/dist/src/utils/core/html-helpers.js +36 -0
  111. package/dist/src/utils/core/index.js +28 -0
  112. package/dist/src/utils/core/logger.js +38 -0
  113. package/dist/src/utils/core/rule-helpers.js +15 -0
  114. package/dist/src/utils/core/workload-formatter.js +6 -0
  115. package/dist/src/utils/shared/array-helpers.js +25 -0
  116. package/dist/src/utils/shared/date-helpers.js +109 -0
  117. package/dist/src/utils/shared/html-helpers.js +37 -0
  118. package/dist/src/utils/shared/index.js +25 -0
  119. package/dist/src/utils/shared/rule-helpers.js +20 -0
  120. package/dist/src/utils/shared/time-formatters.js +76 -0
  121. package/dist/styles.css +2 -0
  122. package/package.json +107 -0
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.analyzeApp = analyzeApp;
37
+ exports.analyzeLib = analyzeLib;
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const core_1 = require("../utils/core");
41
+ /**
42
+ * Analyse les détails d'une application Angular
43
+ */
44
+ function analyzeApp(appName, projectPath, isNxMonorepo) {
45
+ let appPath = projectPath;
46
+ if (isNxMonorepo) {
47
+ appPath = path.join(projectPath, 'apps', appName);
48
+ }
49
+ else {
50
+ const angularJsonPath = path.join(projectPath, 'angular.json');
51
+ if (fs.existsSync(angularJsonPath)) {
52
+ const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf-8'));
53
+ const appConfig = angularJson.projects?.[appName];
54
+ if (appConfig?.root) {
55
+ appPath = path.join(projectPath, appConfig.root);
56
+ }
57
+ else if (appConfig?.sourceRoot) {
58
+ appPath = path.join(projectPath, appConfig.sourceRoot);
59
+ }
60
+ }
61
+ }
62
+ const packageJsonPath = path.join(projectPath, 'package.json');
63
+ let description;
64
+ let dependencies = 0;
65
+ let devDependencies = 0;
66
+ if (fs.existsSync(packageJsonPath)) {
67
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
68
+ description = packageJson.description;
69
+ dependencies = Object.keys(packageJson.dependencies || {}).length;
70
+ devDependencies = Object.keys(packageJson.devDependencies || {}).length;
71
+ }
72
+ const components = (0, core_1.countFiles)(appPath, /\.component\.ts$/);
73
+ const modules = (0, core_1.countFiles)(appPath, /\.module\.ts$/);
74
+ const services = (0, core_1.countFiles)(appPath, /\.service\.ts$/);
75
+ const directives = (0, core_1.countFiles)(appPath, /\.directive\.ts$/);
76
+ const pipes = (0, core_1.countFiles)(appPath, /\.pipe\.ts$/);
77
+ const guards = (0, core_1.countFiles)(appPath, /\.guard\.ts$/);
78
+ const interceptors = (0, core_1.countFiles)(appPath, /\.interceptor\.ts$/);
79
+ const resolvers = (0, core_1.countFiles)(appPath, /\.resolver\.ts$/);
80
+ return {
81
+ name: appName,
82
+ description,
83
+ path: appPath,
84
+ dependencies,
85
+ devDependencies,
86
+ components,
87
+ modules,
88
+ services,
89
+ directives,
90
+ pipes,
91
+ guards,
92
+ interceptors,
93
+ resolvers
94
+ };
95
+ }
96
+ /**
97
+ * Analyse les détails d'une librairie Angular (Nx uniquement)
98
+ */
99
+ function analyzeLib(libName, projectPath) {
100
+ const libPath = path.join(projectPath, 'libs', libName);
101
+ const packageJsonPath = path.join(projectPath, 'package.json');
102
+ let description;
103
+ let dependencies = 0;
104
+ let devDependencies = 0;
105
+ if (fs.existsSync(packageJsonPath)) {
106
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
107
+ description = packageJson.description;
108
+ dependencies = Object.keys(packageJson.dependencies || {}).length;
109
+ devDependencies = Object.keys(packageJson.devDependencies || {}).length;
110
+ }
111
+ const components = (0, core_1.countFiles)(libPath, /\.component\.ts$/);
112
+ const modules = (0, core_1.countFiles)(libPath, /\.module\.ts$/);
113
+ const services = (0, core_1.countFiles)(libPath, /\.service\.ts$/);
114
+ const directives = (0, core_1.countFiles)(libPath, /\.directive\.ts$/);
115
+ const pipes = (0, core_1.countFiles)(libPath, /\.pipe\.ts$/);
116
+ const guards = (0, core_1.countFiles)(libPath, /\.guard\.ts$/);
117
+ const interceptors = (0, core_1.countFiles)(libPath, /\.interceptor\.ts$/);
118
+ const resolvers = (0, core_1.countFiles)(libPath, /\.resolver\.ts$/);
119
+ return {
120
+ name: libName,
121
+ description,
122
+ path: libPath,
123
+ dependencies,
124
+ devDependencies,
125
+ components,
126
+ modules,
127
+ services,
128
+ directives,
129
+ pipes,
130
+ guards,
131
+ interceptors,
132
+ resolvers
133
+ };
134
+ }
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAttributeValue = getAttributeValue;
4
+ exports.matchesAttributePattern = matchesAttributePattern;
5
+ const html_parser_1 = require("./html-parser");
6
+ const matcher_helpers_1 = require("../utils/matcher-helpers");
7
+ /**
8
+ * Récupère le nom d'un attribut
9
+ */
10
+ function getAttributeName(attr) {
11
+ if ('name' in attr) {
12
+ return attr.name;
13
+ }
14
+ if ('key' in attr && 'span' in attr.key) {
15
+ return attr.key.span.toString();
16
+ }
17
+ return '';
18
+ }
19
+ /**
20
+ * Récupère la valeur d'un attribut
21
+ */
22
+ function getAttributeValue(attr) {
23
+ // BoundAttribute (property binding like [ngModel] or [(ngModel)])
24
+ if ('value' in attr && attr.value) {
25
+ if (typeof attr.value === 'string') {
26
+ return attr.value;
27
+ }
28
+ // Pour les inputs bindings, la valeur est dans value.source
29
+ if ('source' in attr.value) {
30
+ return attr.value.source;
31
+ }
32
+ }
33
+ // TextAttribute (regular attribute)
34
+ if ('value' in attr && typeof attr.value === 'string') {
35
+ return attr.value;
36
+ }
37
+ return '';
38
+ }
39
+ /**
40
+ * Vérifie si un attribut/input contient des erreurs de parsing
41
+ * (utilisé pour détecter des assignations interdites dans [(ngModel)])
42
+ */
43
+ function hasBindingErrors(attr) {
44
+ if ('value' in attr && attr.value && 'errors' in attr.value && Array.isArray(attr.value.errors)) {
45
+ return attr.value.errors.length > 0;
46
+ }
47
+ return false;
48
+ }
49
+ /**
50
+ * Matche un pattern Attribute (*ngIf, *ngFor, [(ngModel)], etc.)
51
+ */
52
+ function matchesAttributePattern(element, pattern, htmlContent) {
53
+ const patternNames = (0, matcher_helpers_1.ensureArray)(pattern.name);
54
+ // Vérifier les attributs de l'élément (attributes, inputs, templateAttrs)
55
+ const allAttributes = [
56
+ ...element.attributes,
57
+ ...element.inputs,
58
+ ...(element.templateAttrs || [])
59
+ ];
60
+ for (const attr of allAttributes) {
61
+ let attrName = getAttributeName(attr);
62
+ // Pour les directives structurelles, ajouter le préfixe *
63
+ if (element.templateAttrs && element.templateAttrs.includes(attr)) {
64
+ attrName = '*' + attrName;
65
+ }
66
+ if (patternNames.includes(attrName)) {
67
+ // Vérifier valueMatches si spécifié
68
+ if (pattern.valueMatches) {
69
+ const attrValue = getAttributeValue(attr);
70
+ const valueRegex = new RegExp(pattern.valueMatches);
71
+ // Pour ngModel, on peut aussi utiliser les erreurs de parsing Angular
72
+ const hasErrors = hasBindingErrors(attr);
73
+ const valueMatches = valueRegex.test(attrValue);
74
+ // Si ni la regex ne matche, ni d'erreurs détectées, skip
75
+ if (!valueMatches && !hasErrors) {
76
+ continue;
77
+ }
78
+ }
79
+ return {
80
+ lineNumber: (0, html_parser_1.getLineNumber)(attr, htmlContent),
81
+ matchedText: (0, html_parser_1.getNodeText)(element, htmlContent)
82
+ };
83
+ }
84
+ }
85
+ return null;
86
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesHtmlPatternInComponent = matchesHtmlPatternInComponent;
4
+ const index_1 = require("./index");
5
+ /**
6
+ * Vérifie si un nœud Decorator @Component contient un template HTML qui matche le pattern
7
+ * Extrait le template du @Component et utilise findHtmlPatternMatches
8
+ */
9
+ function matchesHtmlPatternInComponent(node, pattern) {
10
+ // Le nœud doit être un Decorator @Component
11
+ if (!node.getKindName || node.getKindName() !== 'Decorator') {
12
+ return false;
13
+ }
14
+ // Extraire le template du @Component
15
+ const expression = node.getExpression();
16
+ if (!expression || !expression.getKindName || expression.getKindName() !== 'CallExpression') {
17
+ return false;
18
+ }
19
+ const args = expression.getArguments();
20
+ if (args.length === 0) {
21
+ return false;
22
+ }
23
+ const configObject = args[0];
24
+ if (!configObject || configObject.getKindName() !== 'ObjectLiteralExpression') {
25
+ return false;
26
+ }
27
+ const templateProperty = configObject.getProperty('template');
28
+ if (!templateProperty) {
29
+ return false;
30
+ }
31
+ const templateValue = templateProperty.getInitializer();
32
+ if (!templateValue) {
33
+ return false;
34
+ }
35
+ // Extraire le texte du template et retirer les quotes/backticks
36
+ const templateContent = templateValue.getText().replace(/^[`'"]+|[`'"]+$/g, '');
37
+ // Utiliser findHtmlPatternMatches pour vérifier le pattern
38
+ const matches = (0, index_1.findHtmlPatternMatches)(templateContent, pattern);
39
+ return matches.length > 0;
40
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesElementPattern = matchesElementPattern;
4
+ exports.matchesDeferredBlockPattern = matchesDeferredBlockPattern;
5
+ const html_parser_1 = require("./html-parser");
6
+ const matcher_helpers_1 = require("../utils/matcher-helpers");
7
+ /**
8
+ * Matche un pattern Element (ng-content, div, etc.)
9
+ */
10
+ function matchesElementPattern(node, pattern, htmlContent) {
11
+ // Vérifier si c'est un Element ou Content (ng-content)
12
+ const isContentOrElement = node.constructor &&
13
+ (node.constructor.name === 'Element' || node.constructor.name === 'Content');
14
+ if (!isContentOrElement || !node.name) {
15
+ return null;
16
+ }
17
+ const patternNames = (0, matcher_helpers_1.ensureArray)(pattern.name);
18
+ if (!patternNames.includes(node.name)) {
19
+ return null;
20
+ }
21
+ // Vérifier hasChildren si spécifié
22
+ if (pattern.hasChildren !== undefined) {
23
+ // Filtrer les enfants pour exclure les whitespace-only Text nodes
24
+ const nonWhitespaceChildren = node.children?.filter((child) => {
25
+ // Si c'est un Text node, vérifier s'il contient autre chose que du whitespace
26
+ if (child.constructor?.name === 'Text') {
27
+ return child.value && child.value.trim().length > 0;
28
+ }
29
+ // Les autres types de nœuds (Element, Comment, etc.) sont toujours considérés
30
+ return true;
31
+ }) || [];
32
+ const hasChildren = nonWhitespaceChildren.length > 0;
33
+ if (pattern.hasChildren !== hasChildren) {
34
+ return null;
35
+ }
36
+ }
37
+ return {
38
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
39
+ matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
40
+ };
41
+ }
42
+ /**
43
+ * Matche un pattern DeferredBlock (@defer blocks)
44
+ */
45
+ function matchesDeferredBlockPattern(node, htmlContent) {
46
+ // Vérifier si c'est un DeferredBlock
47
+ if (node.constructor && node.constructor.name === 'DeferredBlock') {
48
+ return {
49
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
50
+ matchedText: (0, html_parser_1.getNodeText)(node, htmlContent).substring(0, 100)
51
+ };
52
+ }
53
+ return null;
54
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseHtmlTemplate = parseHtmlTemplate;
4
+ exports.getLineNumber = getLineNumber;
5
+ exports.getNodeText = getNodeText;
6
+ exports.isElement = isElement;
7
+ const compiler_1 = require("@angular/compiler");
8
+ /**
9
+ * Parse un template HTML Angular et retourne l'AST
10
+ * NOTE: Retourne les nœuds même en présence d'erreurs de parsing
11
+ * (certaines erreurs sont précisément ce qu'on cherche à détecter)
12
+ * Support complet Angular 18-20: @if, @for, @switch, @defer, @let
13
+ */
14
+ function parseHtmlTemplate(htmlContent) {
15
+ try {
16
+ const parsed = (0, compiler_1.parseTemplate)(htmlContent, '', {
17
+ preserveWhitespaces: false,
18
+ interpolationConfig: undefined,
19
+ enableBlockSyntax: true, // Support @if, @for, @switch, @defer
20
+ enableLetSyntax: true // Support @let
21
+ });
22
+ return parsed.nodes;
23
+ }
24
+ catch (error) {
25
+ return [];
26
+ }
27
+ }
28
+ /**
29
+ * Trouve le numéro de ligne d'un nœud dans le template
30
+ */
31
+ function getLineNumber(node, htmlContent) {
32
+ if (node.sourceSpan && node.sourceSpan.start && node.sourceSpan.start.line !== undefined) {
33
+ return node.sourceSpan.start.line + 1; // Angular utilise 0-indexed
34
+ }
35
+ // Fallback: estimer via l'offset
36
+ if (node.sourceSpan && node.sourceSpan.start && node.sourceSpan.start.offset !== undefined) {
37
+ const textBeforeNode = htmlContent.substring(0, node.sourceSpan.start.offset);
38
+ return textBeforeNode.split('\n').length;
39
+ }
40
+ return 1;
41
+ }
42
+ /**
43
+ * Extrait le texte d'un nœud
44
+ */
45
+ function getNodeText(node, htmlContent) {
46
+ if (node.sourceSpan && node.sourceSpan.start && node.sourceSpan.end) {
47
+ const start = node.sourceSpan.start.offset;
48
+ const end = node.sourceSpan.end.offset;
49
+ return htmlContent.substring(start, end).trim();
50
+ }
51
+ return '';
52
+ }
53
+ /**
54
+ * Vérifie si un nœud est un Element
55
+ */
56
+ function isElement(node) {
57
+ return 'attributes' in node && 'inputs' in node;
58
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesPipePattern = matchesPipePattern;
4
+ exports.traversePipeExpression = traversePipeExpression;
5
+ const html_parser_1 = require("./html-parser");
6
+ /**
7
+ * Extrait la valeur d'un argument de pipe
8
+ */
9
+ function getPipeArgumentValue(arg) {
10
+ if (!arg)
11
+ return '';
12
+ // LiteralPrimitive (string literal dans l'AST)
13
+ if ('value' in arg && typeof arg.value === 'string') {
14
+ return arg.value;
15
+ }
16
+ // Pour autres types d'expressions, utiliser source si disponible
17
+ if ('source' in arg && typeof arg.source === 'string') {
18
+ return arg.source;
19
+ }
20
+ // Fallback: essayer d'extraire le texte brut
21
+ if (typeof arg === 'string') {
22
+ return arg;
23
+ }
24
+ return '';
25
+ }
26
+ /**
27
+ * Matche un pattern PipeExpression (| async, | date, etc.)
28
+ */
29
+ function matchesPipePattern(node, pattern, htmlContent) {
30
+ if (!pattern.pipeName) {
31
+ return null;
32
+ }
33
+ // Vérifier si c'est un BindingPipe avec le bon nom
34
+ if (!node.name || node.name !== pattern.pipeName) {
35
+ return null;
36
+ }
37
+ // Si le pattern spécifie des contraintes sur les arguments de pipe
38
+ if (pattern.pipeArgs && pattern.pipeArgs.formatMatches) {
39
+ // Vérifier que la pipe a des arguments
40
+ if (!node.args || node.args.length === 0) {
41
+ return null;
42
+ }
43
+ const firstArgValue = getPipeArgumentValue(node.args[0]);
44
+ const formatRegex = new RegExp(pattern.pipeArgs.formatMatches);
45
+ // Matcher uniquement si le format correspond
46
+ if (!formatRegex.test(firstArgValue)) {
47
+ return null;
48
+ }
49
+ return {
50
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
51
+ matchedText: (0, html_parser_1.getNodeText)(node, htmlContent).substring(0, 100)
52
+ };
53
+ }
54
+ // Pas de contrainte sur les args, match basé sur le nom de pipe seul
55
+ const nodeText = (0, html_parser_1.getNodeText)(node, htmlContent);
56
+ const pipeRegex = new RegExp(`\\|\\s*${pattern.pipeName}\\b`);
57
+ if (pipeRegex.test(nodeText)) {
58
+ return {
59
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
60
+ matchedText: nodeText.substring(0, 100)
61
+ };
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Parcourt récursivement une expression AST à la recherche de BindingPipe
67
+ */
68
+ function traversePipeExpression(expr, pattern, htmlContent, matches) {
69
+ if (!expr)
70
+ return;
71
+ // Si pattern cherche une PipeExpression, chercher BindingPipe
72
+ if (pattern.nodeType === 'PipeExpression' && pattern.pipeName) {
73
+ if (expr.constructor && expr.constructor.name === 'BindingPipe') {
74
+ const match = matchesPipePattern(expr, pattern, htmlContent);
75
+ if (match) {
76
+ matches.push(match);
77
+ }
78
+ }
79
+ }
80
+ // Parcourir les expressions d'une Interpolation
81
+ if (expr.expressions && Array.isArray(expr.expressions)) {
82
+ for (const subExpr of expr.expressions) {
83
+ traversePipeExpression(subExpr, pattern, htmlContent, matches);
84
+ }
85
+ }
86
+ // Parcourir récursivement les sous-expressions
87
+ if (expr.exp) {
88
+ traversePipeExpression(expr.exp, pattern, htmlContent, matches);
89
+ }
90
+ if (expr.args && Array.isArray(expr.args)) {
91
+ for (const arg of expr.args) {
92
+ traversePipeExpression(arg, pattern, htmlContent, matches);
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesBoundTextPattern = matchesBoundTextPattern;
4
+ const html_parser_1 = require("./html-parser");
5
+ /**
6
+ * Matche un pattern BoundText (interpolations {{ ... }})
7
+ */
8
+ function matchesBoundTextPattern(node, pattern, htmlContent) {
9
+ // Vérifier si c'est bien un BoundText (interpolation) et non un Text simple
10
+ if (!('value' in node) || !node.value || typeof node.value !== 'object') {
11
+ return null;
12
+ }
13
+ // Vérifier textMatches si spécifié
14
+ if (!pattern.textMatches) {
15
+ return null;
16
+ }
17
+ const textRegex = new RegExp(pattern.textMatches);
18
+ // value.ast contient l'AST de l'expression (Interpolation, etc.)
19
+ const ast = node.value.ast;
20
+ // Si l'AST est une Interpolation avec expressions
21
+ if (ast && ast.expressions && Array.isArray(ast.expressions)) {
22
+ for (const expr of ast.expressions) {
23
+ // Pour LiteralPrimitive (mots-clés comme 'in')
24
+ if (expr.value !== undefined && typeof expr.value === 'string') {
25
+ if (textRegex.test(expr.value)) {
26
+ return {
27
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
28
+ matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
29
+ };
30
+ }
31
+ }
32
+ // Pour VoidExpression (Angular parse 'void' comme opérateur)
33
+ if (expr.constructor && expr.constructor.name === 'VoidExpression') {
34
+ if (textRegex.test('void')) {
35
+ return {
36
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
37
+ matchedText: (0, html_parser_1.getNodeText)(node, htmlContent)
38
+ };
39
+ }
40
+ }
41
+ }
42
+ }
43
+ // Fallback: tester le source complet
44
+ if ('source' in node.value && typeof node.value.source === 'string') {
45
+ if (textRegex.test(node.value.source)) {
46
+ return {
47
+ lineNumber: (0, html_parser_1.getLineNumber)(node, htmlContent),
48
+ matchedText: node.value.source
49
+ };
50
+ }
51
+ }
52
+ return null;
53
+ }
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesHtmlPatternInComponent = void 0;
4
+ exports.findHtmlPatternMatches = findHtmlPatternMatches;
5
+ exports.isHtmlPattern = isHtmlPattern;
6
+ const html_parser_1 = require("./html-parser");
7
+ const html_attribute_matcher_1 = require("./html-attribute-matcher");
8
+ const html_element_matcher_1 = require("./html-element-matcher");
9
+ const html_text_matcher_1 = require("./html-text-matcher");
10
+ const html_pipe_matcher_1 = require("./html-pipe-matcher");
11
+ /**
12
+ * Vérifie si un nœud HTML correspond à un pattern AST HTML
13
+ * Supporte : Attribute, PipeExpression, BoundText, Element, DeferredBlock
14
+ */
15
+ function matchesHtmlNode(node, pattern, htmlContent) {
16
+ // Pattern: BoundText (interpolations {{ ... }})
17
+ if (pattern.nodeType === 'BoundText') {
18
+ return (0, html_text_matcher_1.matchesBoundTextPattern)(node, pattern, htmlContent);
19
+ }
20
+ // Pattern: Attribute (*ngIf, *ngFor, etc.)
21
+ if (pattern.nodeType === 'Attribute' && (0, html_parser_1.isElement)(node)) {
22
+ return (0, html_attribute_matcher_1.matchesAttributePattern)(node, pattern, htmlContent);
23
+ }
24
+ // Pattern: Element (ng-content, div, etc.)
25
+ if (pattern.nodeType === 'Element') {
26
+ return (0, html_element_matcher_1.matchesElementPattern)(node, pattern, htmlContent);
27
+ }
28
+ // Pattern: DeferredBlock (@defer blocks)
29
+ if (pattern.nodeType === 'DeferredBlock') {
30
+ return (0, html_element_matcher_1.matchesDeferredBlockPattern)(node, htmlContent);
31
+ }
32
+ // Pattern: PipeExpression (| async, | date, etc.)
33
+ if (pattern.nodeType === 'PipeExpression' && pattern.pipeName) {
34
+ return (0, html_pipe_matcher_1.matchesPipePattern)(node, pattern, htmlContent);
35
+ }
36
+ return null;
37
+ }
38
+ /**
39
+ * Parcourt récursivement l'AST HTML et trouve les matches
40
+ */
41
+ function traverseHtmlAst(nodes, pattern, htmlContent, matches) {
42
+ for (const node of nodes) {
43
+ // Vérifier si le nœud matche
44
+ const match = matchesHtmlNode(node, pattern, htmlContent);
45
+ if (match) {
46
+ matches.push(match);
47
+ }
48
+ // Si pattern cherche PipeExpression et node est BoundText, parcourir son expression
49
+ if (pattern.nodeType === 'PipeExpression' && 'value' in node && node.value) {
50
+ const value = node.value;
51
+ // value est ASTWithSource, l'AST réel est dans value.ast
52
+ if (value.ast) {
53
+ (0, html_pipe_matcher_1.traversePipeExpression)(value.ast, pattern, htmlContent, matches);
54
+ }
55
+ else {
56
+ (0, html_pipe_matcher_1.traversePipeExpression)(value, pattern, htmlContent, matches);
57
+ }
58
+ }
59
+ // 1. Parcourir les enfants standards
60
+ if ('children' in node && Array.isArray(node.children)) {
61
+ traverseHtmlAst(node.children, pattern, htmlContent, matches);
62
+ }
63
+ // 2. Parcourir les branches (@if, @else if, @else)
64
+ if ('branches' in node && Array.isArray(node.branches)) {
65
+ for (const branch of node.branches) {
66
+ if (branch.children && Array.isArray(branch.children)) {
67
+ traverseHtmlAst(branch.children, pattern, htmlContent, matches);
68
+ }
69
+ }
70
+ }
71
+ // 3. Parcourir les cases (@switch, @case, @default)
72
+ if ('cases' in node && Array.isArray(node.cases)) {
73
+ for (const caseNode of node.cases) {
74
+ if (caseNode.children && Array.isArray(caseNode.children)) {
75
+ traverseHtmlAst(caseNode.children, pattern, htmlContent, matches);
76
+ }
77
+ }
78
+ }
79
+ // 4. Parcourir @for @empty
80
+ if ('empty' in node && node.empty?.children) {
81
+ traverseHtmlAst(node.empty.children, pattern, htmlContent, matches);
82
+ }
83
+ // 5-8. Parcourir @defer sub-blocks (mainBlock, placeholder, loading, error)
84
+ const deferSubBlocks = ['mainBlock', 'placeholder', 'loading', 'error'];
85
+ for (const blockName of deferSubBlocks) {
86
+ if (blockName in node && node[blockName]?.children) {
87
+ traverseHtmlAst(node[blockName].children, pattern, htmlContent, matches);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ /**
93
+ * Trouve les matches d'un pattern AST HTML dans un template
94
+ * @param htmlContent Contenu du template HTML
95
+ * @param pattern Pattern AST à rechercher
96
+ * @returns Liste des matches trouvés
97
+ */
98
+ function findHtmlPatternMatches(htmlContent, pattern) {
99
+ const matches = [];
100
+ // Parser le template HTML
101
+ const ast = (0, html_parser_1.parseHtmlTemplate)(htmlContent);
102
+ // Parcourir l'AST et trouver les matches
103
+ traverseHtmlAst(ast, pattern, htmlContent, matches);
104
+ return matches;
105
+ }
106
+ /**
107
+ * Vérifie si un pattern est un pattern HTML
108
+ * (nécessite @angular/compiler)
109
+ */
110
+ function isHtmlPattern(pattern) {
111
+ return pattern.nodeType === 'Attribute' ||
112
+ pattern.nodeType === 'PipeExpression' ||
113
+ pattern.nodeType === 'BoundText' ||
114
+ pattern.nodeType === 'Element' ||
115
+ pattern.nodeType === 'DeferredBlock';
116
+ }
117
+ var html_component_matcher_1 = require("./html-component-matcher");
118
+ Object.defineProperty(exports, "matchesHtmlPatternInComponent", { enumerable: true, get: function () { return html_component_matcher_1.matchesHtmlPatternInComponent; } });