devextreme-schematics 1.11.2 → 1.12.1

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 (32) hide show
  1. package/README.md +34 -6
  2. package/package.json +6 -3
  3. package/src/add-app-template/index_spec.js +24 -1
  4. package/src/add-app-template/index_spec.js.map +1 -1
  5. package/src/add-app-template/schema.json +1 -1
  6. package/src/add-layout/index_spec.js +24 -1
  7. package/src/add-layout/index_spec.js.map +1 -1
  8. package/src/add-sample-views/index_spec.js +24 -1
  9. package/src/add-sample-views/index_spec.js.map +1 -1
  10. package/src/add-view/index_spec.js +24 -1
  11. package/src/add-view/index_spec.js.map +1 -1
  12. package/src/collection.json +5 -0
  13. package/src/install/index_spec.js +24 -1
  14. package/src/install/index_spec.js.map +1 -1
  15. package/src/install/schema.json +1 -1
  16. package/src/migrate-config-components/README.md +74 -0
  17. package/src/migrate-config-components/index.d.ts +7 -0
  18. package/src/migrate-config-components/index.js +71 -0
  19. package/src/migrate-config-components/index.js.map +1 -0
  20. package/src/migrate-config-components/index.ts +77 -0
  21. package/src/migrate-config-components/mappings/deprecated-config-map.json +1088 -0
  22. package/src/migrate-config-components/schema.json +28 -0
  23. package/src/migrate-config-components/template-migrator.d.ts +22 -0
  24. package/src/migrate-config-components/template-migrator.js +301 -0
  25. package/src/migrate-config-components/template-migrator.js.map +1 -0
  26. package/src/migrate-config-components/template-migrator.ts +320 -0
  27. package/src/utility/latest-versions.js +2 -2
  28. package/src/utility/latest-versions.ts +2 -2
  29. package/src/utility/typescript-resolver.d.ts +12 -0
  30. package/src/utility/typescript-resolver.js +153 -0
  31. package/src/utility/typescript-resolver.js.map +1 -0
  32. package/src/utility/typescript-resolver.ts +144 -0
@@ -0,0 +1,320 @@
1
+ import { Tree } from '@angular-devkit/schematics';
2
+ import * as parse5 from 'parse5';
3
+ import picomatch from 'picomatch';
4
+ import { resolveTypeScript } from '../utility/typescript-resolver';
5
+
6
+ // Dynamically require TypeScript if available; skip inline template migration if not.
7
+ // Uses a 3-level fallback: project search -> global search -> temporary install
8
+ const tsResolution = resolveTypeScript();
9
+ const ts: any = tsResolution.ts;
10
+ const tsResolutionErrors: string[] = tsResolution.errors;
11
+
12
+ // Minimal parse5 types for our usage
13
+ interface P5Node { [k: string]: any; }
14
+ interface P5Element extends P5Node { tagName?: string; }
15
+ interface P5Document extends P5Node {
16
+ documentElement?: P5Element;
17
+ }
18
+
19
+ export interface HostRule {
20
+ hostSelector: string;
21
+ configMap: Record<string, string>;
22
+ }
23
+
24
+ export interface RunnerOptions {
25
+ includeGlobs: string[];
26
+ rules: HostRule[];
27
+ }
28
+
29
+ export interface ExecOptions {
30
+ dryRun: boolean;
31
+ logger: { info: (s: string) => void; warn: (s: string) => void };
32
+ }
33
+
34
+ // Transform external HTML template files matched by includeGlobs.
35
+ export async function applyHostAwareTemplateMigrations(
36
+ tree: Tree,
37
+ runner: RunnerOptions,
38
+ exec: ExecOptions
39
+ ): Promise<void> {
40
+ const matcher = picomatch(runner.includeGlobs.length ? runner.includeGlobs : ['**/*.html']);
41
+
42
+ tree.visit(filePath => {
43
+ if (!matcher(filePath)) {
44
+ return;
45
+ }
46
+ if (!filePath.endsWith('.html')) {
47
+ return;
48
+ }
49
+
50
+ const buffer = tree.read(filePath);
51
+ if (!buffer) {
52
+ return;
53
+ }
54
+
55
+ const source = buffer.toString('utf8');
56
+ const { updated, changeCount } = transformTemplate(source, runner.rules);
57
+ if (changeCount === 0) {
58
+ return;
59
+ }
60
+ if (exec.dryRun) {
61
+ exec.logger.info(`[dry] ${filePath} → ${changeCount} changes`);
62
+ } else {
63
+ exec.logger.info(`${filePath} → ${changeCount} changes`);
64
+ tree.overwrite(filePath, updated);
65
+ }
66
+ });
67
+ }
68
+
69
+ // Apply migrations inside inline component templates found in TS/JS files.
70
+ export async function applyInlineComponentTemplateMigrations(
71
+ tree: Tree,
72
+ runner: RunnerOptions,
73
+ exec: ExecOptions,
74
+ scriptGlobs: string[]
75
+ ): Promise<void> {
76
+ if (!scriptGlobs.length) {
77
+ return;
78
+ }
79
+ if (!ts) {
80
+ const errorDetails = tsResolutionErrors.length
81
+ ? `Resolution attempts:\n${tsResolutionErrors.map(e => ' - ' + e).join('\n')}\n`
82
+ : '';
83
+ exec.logger.warn(
84
+ '[config-migrator] Failed to import TypeScript. Skipping inline template migration.\n' +
85
+ errorDetails +
86
+ 'The schematic attempted to import TypeScript from the following locations:\n' +
87
+ ' 1. Project node_modules\n' +
88
+ ' 2. Global node_modules\n' +
89
+ ' 3. Temporary installation (npm cache)\n\n' +
90
+ 'To resolve this issue, install TypeScript.\n\n' +
91
+ 'Project install:\n' +
92
+ ' npm install typescript --save-dev\n\n' +
93
+ 'Global install:\n' +
94
+ ' npm install -g typescript\n\n'
95
+ );
96
+ return;
97
+ }
98
+ const matcher = picomatch(scriptGlobs);
99
+ tree.visit(filePath => {
100
+ if (!matcher(filePath)) {
101
+ return;
102
+ }
103
+ if (!(filePath.endsWith('.ts') || filePath.endsWith('.js'))) {
104
+ return;
105
+ }
106
+
107
+ const buffer = tree.read(filePath);
108
+ if (!buffer) {
109
+ return;
110
+ }
111
+ const sourceText = buffer.toString('utf8');
112
+
113
+ const sf = ts.createSourceFile(
114
+ filePath, sourceText, ts.ScriptTarget.ES2022, true,
115
+ filePath.endsWith('.ts') ? ts.ScriptKind.TS : ts.ScriptKind.JS);
116
+
117
+ interface TemplateEdit { start: number; end: number; text: string; changes: number; }
118
+ const edits: TemplateEdit[] = [];
119
+
120
+ function visit(node: any) {
121
+ if (ts.isDecorator(node) && ts.isCallExpression(node.expression)) {
122
+ const call = node.expression;
123
+ if (ts.isIdentifier(call.expression) && call.expression.text === 'Component' && call.arguments.length) {
124
+ const arg = call.arguments[0];
125
+ if (ts.isObjectLiteralExpression(arg)) {
126
+ for (const prop of arg.properties) {
127
+ if (!ts.isPropertyAssignment(prop)) {
128
+ continue;
129
+ }
130
+ const name = prop.name;
131
+ const propName = ts.isIdentifier(name) ? name.text : ts.isStringLiteral(name) ? name.text : undefined;
132
+ if (propName !== 'template') {
133
+ continue;
134
+ }
135
+ const init = prop.initializer;
136
+ if (ts.isStringLiteral(init)) {
137
+ const raw = init.text;
138
+ const { updated, changeCount } = transformTemplate(raw, runner.rules);
139
+ if (changeCount > 0) {
140
+ const quote = init.getText().startsWith("'") ? '"' : init.getText()[0];
141
+ const newLiteral = quote + updated.replace(new RegExp(quote, 'g'), '\\' + quote) + quote;
142
+ edits.push({ start: init.getStart(), end: init.getEnd(), text: newLiteral, changes: changeCount });
143
+ }
144
+ } else if (ts.isNoSubstitutionTemplateLiteral(init)) {
145
+ const raw = init.text;
146
+ const { updated, changeCount } = transformTemplate(raw, runner.rules);
147
+ if (changeCount > 0) {
148
+ const newLiteral = '`' + escapeBackticks(updated) + '`';
149
+ edits.push({ start: init.getStart(), end: init.getEnd(), text: newLiteral, changes: changeCount });
150
+ }
151
+ } else if (ts.isTemplateExpression(init)) {
152
+ const { placeholderContent, placeholders } = flattenTemplateExpression(init, sourceText);
153
+ const { updated, changeCount } = transformTemplate(placeholderContent, runner.rules);
154
+ if (changeCount > 0) {
155
+ let rebuilt = updated;
156
+ placeholders.forEach(placeholder => {
157
+ rebuilt = rebuilt.replace(new RegExp(placeholder.token, 'g'), placeholder.fullText);
158
+ });
159
+ const newLiteral = '`' + escapeBackticks(rebuilt) + '`';
160
+ edits.push({ start: init.getStart(), end: init.getEnd(), text: newLiteral, changes: changeCount });
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ ts.forEachChild(node, visit);
168
+ }
169
+ visit(sf);
170
+
171
+ if (!edits.length) {
172
+ return;
173
+ }
174
+ edits.sort((a, b) => b.start - a.start); // edits from last to first
175
+ let updatedFile = sourceText;
176
+ for (const edit of edits) {
177
+ updatedFile = updatedFile.slice(0, edit.start) + edit.text + updatedFile.slice(edit.end);
178
+ }
179
+ const totalChanges = edits.reduce((acc, edit) => acc + edit.changes, 0);
180
+ if (exec.dryRun) {
181
+ exec.logger.info(
182
+ `[dry] ${filePath} (inline templates) → ${totalChanges} changes in ${edits.length} template(s)`);
183
+ } else {
184
+ exec.logger.info(
185
+ `${filePath} (inline templates) → ${totalChanges} changes in ${edits.length} template(s)`);
186
+ tree.overwrite(filePath, updatedFile);
187
+ }
188
+ });
189
+ }
190
+
191
+ export function transformTemplate(
192
+ source: string,
193
+ rules: RunnerOptions['rules']
194
+ ): { updated: string; changeCount: number } {
195
+ const document = parse5.parse(source, { sourceCodeLocationInfo: true }) as unknown as P5Document;
196
+ const replacements: Array<{ start: number; end: number; text: string }> = [];
197
+
198
+ const hostSelectorSet = new Set(rules.map(rule => rule.hostSelector));
199
+
200
+ const seenOpens = new Set<number>();
201
+ const seenCloses = new Set<number>();
202
+
203
+ function walkHost(root: P5Element, visitFn: (node: P5Node) => void) {
204
+ function recursiveWalk(node: P5Node) {
205
+ visitFn(node);
206
+ const childNodes = (node as any).childNodes as P5Node[] | undefined;
207
+ if (!childNodes) {
208
+ return;
209
+ }
210
+ for (const childNode of childNodes) {
211
+ if (isElement(childNode) && hostSelectorSet.has((childNode as any).tagName) && childNode !== root) {
212
+ continue;
213
+ }
214
+ recursiveWalk(childNode);
215
+ }
216
+ }
217
+ recursiveWalk(root);
218
+ }
219
+
220
+ for (const rule of rules) {
221
+ const hosts = findElementsByTag(document, rule.hostSelector);
222
+ for (const host of hosts) {
223
+ if (!(host as any).sourceCodeLocation) {
224
+ continue;
225
+ }
226
+ walkHost(host, (node) => {
227
+ if (!isElement(node)) {
228
+ return;
229
+ }
230
+ const oldName: string | undefined = (node as any).tagName;
231
+ if (!oldName) {
232
+ return;
233
+ }
234
+ const newName = rule.configMap[oldName];
235
+ if (!newName) {
236
+ return;
237
+ }
238
+ const loc: any = (node as any).sourceCodeLocation;
239
+ if (!loc) {
240
+ return;
241
+ }
242
+ if (loc.startTag) {
243
+ const openStart = loc.startTag.startOffset + 1;
244
+ const openEnd = openStart + oldName.length;
245
+ if (!seenOpens.has(openStart)) {
246
+ seenOpens.add(openStart);
247
+ replacements.push({ start: openStart, end: openEnd, text: newName });
248
+ }
249
+ }
250
+ if (loc.endTag) {
251
+ const endStart = loc.endTag.startOffset + 2;
252
+ const endEnd = endStart + oldName.length;
253
+ if (!seenCloses.has(endStart)) {
254
+ seenCloses.add(endStart);
255
+ replacements.push({ start: endStart, end: endEnd, text: newName });
256
+ }
257
+ }
258
+ });
259
+ }
260
+ }
261
+
262
+ if (!replacements.length) {
263
+ return { updated: source, changeCount: 0 };
264
+ }
265
+ replacements.sort((a, b) => b.start - a.start);
266
+ let updated = source;
267
+ for (const replacement of replacements) {
268
+ updated = updated.slice(0, replacement.start) + replacement.text + updated.slice(replacement.end);
269
+ }
270
+ return { updated, changeCount: replacements.length };
271
+ }
272
+
273
+ // Helpers
274
+
275
+ function flattenTemplateExpression(
276
+ node: any,
277
+ source: string
278
+ ): { placeholderContent: string; placeholders: Array<{ token: string; fullText: string }> } {
279
+ const placeholders: Array<{ token: string; fullText: string }> = [];
280
+ let content = node.head.text;
281
+ node.templateSpans.forEach((span: any, i: number) => {
282
+ const token = `__NG_EXPR_PLACEHOLDER_${i}__`;
283
+ const fullText = '${' + source.slice(span.expression.getStart(), span.expression.getEnd()) + '}';
284
+ placeholders.push({ token, fullText });
285
+ content += token + span.literal.text;
286
+ });
287
+ return { placeholderContent: content, placeholders };
288
+ }
289
+
290
+ function escapeBackticks(text: string): string {
291
+ // First escape backslashes, then escape backticks
292
+ return text.replace(/\\/g, '\\\\').replace(/`/g, '\\`');
293
+ }
294
+
295
+ // Utilities
296
+
297
+ function isElement(n: P5Node): n is P5Element {
298
+ return (n as any).tagName !== undefined;
299
+ }
300
+
301
+ function walk(node: P5Node, visit: (n: P5Node) => void) {
302
+ visit(node);
303
+ const childNodes = (node as any).childNodes as P5Node[] | undefined;
304
+ if (!childNodes) {
305
+ return;
306
+ }
307
+ for (const childNode of childNodes) {
308
+ walk(childNode, visit);
309
+ }
310
+ }
311
+
312
+ function findElementsByTag(doc: P5Document | P5Element, tag: string): P5Element[] {
313
+ const result: P5Element[] = [];
314
+ walk(doc as unknown as P5Node, (node) => {
315
+ if (isElement(node) && node.tagName === tag) {
316
+ result.push(node);
317
+ }
318
+ });
319
+ return result;
320
+ }
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.latestVersions = void 0;
4
4
  // TODO: implement
5
5
  exports.latestVersions = {
6
- 'devextreme': '25.1.5',
7
- 'devextreme-angular': '25.1.5',
6
+ 'devextreme': '25.1.6',
7
+ 'devextreme-angular': '25.1.6',
8
8
  'devextreme-cli': 'latest',
9
9
  'sass-embedded': '1.66.0'
10
10
  };
@@ -1,7 +1,7 @@
1
1
  // TODO: implement
2
2
  export const latestVersions = {
3
- 'devextreme': '25.1.5',
4
- 'devextreme-angular': '25.1.5',
3
+ 'devextreme': '25.1.6',
4
+ 'devextreme-angular': '25.1.6',
5
5
  'devextreme-cli': 'latest',
6
6
  'sass-embedded': '1.66.0'
7
7
  };
@@ -0,0 +1,12 @@
1
+ export interface TypeScriptResolutionResult {
2
+ ts: any | null;
3
+ resolutionMethod: 'project' | 'global' | 'temporary' | null;
4
+ errors: string[];
5
+ }
6
+ /**
7
+ * Resolves TypeScript with a 3-level fallback strategy:
8
+ * 1. Project search - look in the user's project node_modules
9
+ * 2. Global search - look in global node_modules
10
+ * 3. Temporary install - use npx to temporarily install typescript
11
+ */
12
+ export declare function resolveTypeScript(): TypeScriptResolutionResult;
@@ -0,0 +1,153 @@
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 (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.resolveTypeScript = void 0;
27
+ const child_process_1 = require("child_process");
28
+ const path = __importStar(require("path"));
29
+ const fs = __importStar(require("fs"));
30
+ /**
31
+ * Resolves TypeScript with a 3-level fallback strategy:
32
+ * 1. Project search - look in the user's project node_modules
33
+ * 2. Global search - look in global node_modules
34
+ * 3. Temporary install - use npx to temporarily install typescript
35
+ */
36
+ function resolveTypeScript() {
37
+ const errors = [];
38
+ try {
39
+ const projectTs = tryResolveFromProject();
40
+ if (projectTs) {
41
+ return {
42
+ ts: projectTs,
43
+ resolutionMethod: 'project',
44
+ errors: []
45
+ };
46
+ }
47
+ }
48
+ catch (err) {
49
+ errors.push(`Project resolution failed: ${(err === null || err === void 0 ? void 0 : err.message) || err}`);
50
+ }
51
+ try {
52
+ const globalTs = tryResolveFromGlobal();
53
+ if (globalTs) {
54
+ return {
55
+ ts: globalTs,
56
+ resolutionMethod: 'global',
57
+ errors: []
58
+ };
59
+ }
60
+ }
61
+ catch (err) {
62
+ errors.push(`Global resolution failed: ${(err === null || err === void 0 ? void 0 : err.message) || err}`);
63
+ }
64
+ try {
65
+ const tempTs = tryResolveViaTemporaryInstall();
66
+ if (tempTs) {
67
+ return {
68
+ ts: tempTs,
69
+ resolutionMethod: 'temporary',
70
+ errors: []
71
+ };
72
+ }
73
+ }
74
+ catch (err) {
75
+ errors.push(`Temporary install failed: ${(err === null || err === void 0 ? void 0 : err.message) || err}`);
76
+ }
77
+ return {
78
+ ts: null,
79
+ resolutionMethod: null,
80
+ errors
81
+ };
82
+ }
83
+ exports.resolveTypeScript = resolveTypeScript;
84
+ function tryResolveFromProject() {
85
+ var _a;
86
+ const searchPaths = [
87
+ process.cwd(),
88
+ path.dirname(((_a = require.main) === null || _a === void 0 ? void 0 : _a.filename) || ''),
89
+ ];
90
+ for (const searchPath of searchPaths) {
91
+ try {
92
+ const tsPath = require.resolve('typescript', { paths: [searchPath] });
93
+ // tslint:disable-next-line:no-var-requires
94
+ return require(tsPath);
95
+ }
96
+ catch (_b) {
97
+ // Continue to next path
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+ function tryResolveFromGlobal() {
103
+ try {
104
+ const globalPath = (0, child_process_1.execSync)('npm root -g', { encoding: 'utf8' }).trim();
105
+ const typescriptPath = path.join(globalPath, 'typescript');
106
+ if (fs.existsSync(typescriptPath)) {
107
+ // tslint:disable-next-line:no-var-requires
108
+ return require(typescriptPath);
109
+ }
110
+ }
111
+ catch (_a) {
112
+ // Fall through
113
+ }
114
+ return null;
115
+ }
116
+ function tryResolveViaTemporaryInstall() {
117
+ try {
118
+ const tmpDir = path.join(require('os').tmpdir(), 'devextreme-schematics-ts-' + Date.now());
119
+ fs.mkdirSync(tmpDir, { recursive: true });
120
+ const packageJson = {
121
+ name: 'temp-typescript-resolver',
122
+ version: '1.0.0',
123
+ dependencies: {
124
+ typescript: 'latest'
125
+ }
126
+ };
127
+ fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify(packageJson, null, 2));
128
+ (0, child_process_1.execSync)('npm install --silent --no-progress', {
129
+ cwd: tmpDir,
130
+ stdio: 'ignore',
131
+ timeout: 30000
132
+ });
133
+ const typescriptPath = path.join(tmpDir, 'node_modules', 'typescript');
134
+ if (fs.existsSync(typescriptPath)) {
135
+ // tslint:disable-next-line:no-var-requires
136
+ const ts = require(typescriptPath);
137
+ setTimeout(() => {
138
+ try {
139
+ fs.rmSync(tmpDir, { recursive: true, force: true });
140
+ }
141
+ catch (_a) {
142
+ // Ignore cleanup errors
143
+ }
144
+ }, 5000);
145
+ return ts;
146
+ }
147
+ }
148
+ catch (_a) {
149
+ // Fall through
150
+ }
151
+ return null;
152
+ }
153
+ //# sourceMappingURL=typescript-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-resolver.js","sourceRoot":"","sources":["typescript-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ import { execSync } from 'child_process';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+
5
+ export interface TypeScriptResolutionResult {
6
+ ts: any | null;
7
+ resolutionMethod: 'project' | 'global' | 'temporary' | null;
8
+ errors: string[];
9
+ }
10
+
11
+ /**
12
+ * Resolves TypeScript with a 3-level fallback strategy:
13
+ * 1. Project search - look in the user's project node_modules
14
+ * 2. Global search - look in global node_modules
15
+ * 3. Temporary install - use npx to temporarily install typescript
16
+ */
17
+ export function resolveTypeScript(): TypeScriptResolutionResult {
18
+ const errors: string[] = [];
19
+
20
+ try {
21
+ const projectTs = tryResolveFromProject();
22
+ if (projectTs) {
23
+ return {
24
+ ts: projectTs,
25
+ resolutionMethod: 'project',
26
+ errors: []
27
+ };
28
+ }
29
+ } catch (err) {
30
+ errors.push(`Project resolution failed: ${err?.message || err}`);
31
+ }
32
+
33
+ try {
34
+ const globalTs = tryResolveFromGlobal();
35
+ if (globalTs) {
36
+ return {
37
+ ts: globalTs,
38
+ resolutionMethod: 'global',
39
+ errors: []
40
+ };
41
+ }
42
+ } catch (err) {
43
+ errors.push(`Global resolution failed: ${err?.message || err}`);
44
+ }
45
+
46
+ try {
47
+ const tempTs = tryResolveViaTemporaryInstall();
48
+ if (tempTs) {
49
+ return {
50
+ ts: tempTs,
51
+ resolutionMethod: 'temporary',
52
+ errors: []
53
+ };
54
+ }
55
+ } catch (err) {
56
+ errors.push(`Temporary install failed: ${err?.message || err}`);
57
+ }
58
+
59
+ return {
60
+ ts: null,
61
+ resolutionMethod: null,
62
+ errors
63
+ };
64
+ }
65
+
66
+ function tryResolveFromProject(): any {
67
+ const searchPaths = [
68
+ process.cwd(),
69
+ path.dirname(require.main?.filename || ''),
70
+ ];
71
+
72
+ for (const searchPath of searchPaths) {
73
+ try {
74
+ const tsPath = require.resolve('typescript', { paths: [searchPath] });
75
+ // tslint:disable-next-line:no-var-requires
76
+ return require(tsPath);
77
+ } catch {
78
+ // Continue to next path
79
+ }
80
+ }
81
+
82
+ return null;
83
+ }
84
+
85
+ function tryResolveFromGlobal(): any {
86
+ try {
87
+ const globalPath = execSync('npm root -g', { encoding: 'utf8' }).trim();
88
+ const typescriptPath = path.join(globalPath, 'typescript');
89
+
90
+ if (fs.existsSync(typescriptPath)) {
91
+ // tslint:disable-next-line:no-var-requires
92
+ return require(typescriptPath);
93
+ }
94
+ } catch {
95
+ // Fall through
96
+ }
97
+
98
+ return null;
99
+ }
100
+
101
+ function tryResolveViaTemporaryInstall(): any {
102
+ try {
103
+ const tmpDir = path.join(require('os').tmpdir(), 'devextreme-schematics-ts-' + Date.now());
104
+ fs.mkdirSync(tmpDir, { recursive: true });
105
+
106
+ const packageJson = {
107
+ name: 'temp-typescript-resolver',
108
+ version: '1.0.0',
109
+ dependencies: {
110
+ typescript: 'latest'
111
+ }
112
+ };
113
+ fs.writeFileSync(
114
+ path.join(tmpDir, 'package.json'),
115
+ JSON.stringify(packageJson, null, 2)
116
+ );
117
+
118
+ execSync('npm install --silent --no-progress', {
119
+ cwd: tmpDir,
120
+ stdio: 'ignore',
121
+ timeout: 30000
122
+ });
123
+
124
+ const typescriptPath = path.join(tmpDir, 'node_modules', 'typescript');
125
+ if (fs.existsSync(typescriptPath)) {
126
+ // tslint:disable-next-line:no-var-requires
127
+ const ts = require(typescriptPath);
128
+
129
+ setTimeout(() => {
130
+ try {
131
+ fs.rmSync(tmpDir, { recursive: true, force: true });
132
+ } catch {
133
+ // Ignore cleanup errors
134
+ }
135
+ }, 5000);
136
+
137
+ return ts;
138
+ }
139
+ } catch {
140
+ // Fall through
141
+ }
142
+
143
+ return null;
144
+ }