rev-dep 1.4.0 → 1.5.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.
@@ -0,0 +1,5 @@
1
+ export declare const babelParsingOptions: {
2
+ errorRecovery: boolean;
3
+ sourceType: string;
4
+ plugins: string[];
5
+ };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.babelParsingOptions = void 0;
4
+ exports.babelParsingOptions = {
5
+ errorRecovery: true,
6
+ sourceType: 'module',
7
+ plugins: [
8
+ 'jsx',
9
+ 'typescript',
10
+ 'objectRestSpread',
11
+ 'classProperties',
12
+ 'asyncGenerators',
13
+ 'decorators-legacy'
14
+ ]
15
+ };
@@ -0,0 +1 @@
1
+ export function groupBy(arr: any, property: any): any;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.groupBy = void 0;
4
+ function groupBy(arr, property) {
5
+ return arr.reduce((result, obj) => {
6
+ const key = obj[property];
7
+ if (!result[key]) {
8
+ result[key] = [];
9
+ }
10
+ result[key].push(obj);
11
+ return result;
12
+ }, {});
13
+ }
14
+ exports.groupBy = groupBy;
@@ -4,19 +4,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const node_path = require('path');
5
5
  const fs = require('fs');
6
6
  const parser = require('@babel/parser');
7
- const template = require('@babel/template').default;
8
7
  const utils_1 = require("../lib/utils");
8
+ const template_1 = require("./template");
9
9
  const SKIP = Symbol('SKIP');
10
+ const babelParsingOptions_1 = require("./babelParsingOptions");
11
+ const groupBy_1 = require("./groupBy");
10
12
  /**
11
13
  *
12
14
  * TODO
13
- * - support imports from baseUrl from TS config
14
- * - persist the original import alias
15
- * - group named imports from the same file
16
- * - handle type imports properly - we don't preserve the import was a type import
17
- * - If that has to be used as a codemod, we have to refactor to make sure we don't change structure of other parts of the code and we preserve imports order
15
+ * + If that has to be used as a codemod, we have to refactor to make sure we don't change structure of other parts of the code and we preserve imports order
16
+ * +- group named imports from the same file
17
+ * + support imports from baseUrl from TS config -> relative | baseUrl | alias
18
+ * + persist the original import alias
19
+ * + allow for a list of files to rewire
20
+ * + use cache for not resolved modules as well
21
+ * + handle type imports properly - we don't preserve the import was a type import
22
+ * + do not touch imports that don't need changes
18
23
  */
19
- module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsConfig)() }) {
24
+ module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsConfig)(), cache = new Map(), includeBarrelExportFiles, excludeBarrelExportFiles = [] }) {
20
25
  const root = tsConfigPath.replace('/tsconfig.json', '');
21
26
  const tsConfigContent = fs.readFileSync(tsConfigPath).toString();
22
27
  const tsConfigContentCleaned = tsConfigContent
@@ -26,9 +31,21 @@ module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsC
26
31
  const tsConfig = JSON.parse(tsConfigContentCleaned);
27
32
  const aliases = tsConfig.compilerOptions.paths;
28
33
  const aliasesKeys = Object.keys(aliases);
29
- const aliasesRegexes = Object.keys(aliases).map((alias) => {
30
- return new RegExp(`^${alias.replace('*', '(.)+')}$`);
31
- });
34
+ const makeRegExpFromAliasExpression = (aliasExpression) => {
35
+ return new RegExp(`^${aliasExpression.replace('*', '(.+)')}$`);
36
+ };
37
+ const aliasesRegexes = Object.keys(aliases).map(makeRegExpFromAliasExpression);
38
+ // TODO we assume that only one aliased path can exist
39
+ const aliasedPathRegExps = Object.values(aliases).map(([fistAliasedPath]) => makeRegExpFromAliasExpression(fistAliasedPath));
40
+ const interpolateAliasWithPath = (aliasKey, aliasedPathRegExp, resolvedSourcePathRelativeToBaseUrl) => {
41
+ const [_, ...groups] = aliasedPathRegExp.exec(resolvedSourcePathRelativeToBaseUrl);
42
+ const aliasParts = aliasKey.split('*');
43
+ const interpolatedAlias = aliasParts.reduce((mergedPath, aliasPart, idx) => {
44
+ var _a;
45
+ return `${mergedPath}${aliasPart}${(_a = groups[idx]) !== null && _a !== void 0 ? _a : ''}`;
46
+ }, '');
47
+ return interpolatedAlias;
48
+ };
32
49
  let baseUrlDirs = [];
33
50
  const baseUrl = tsConfig.compilerOptions.baseUrl;
34
51
  if (baseUrl) {
@@ -39,10 +56,10 @@ module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsC
39
56
  .map((dirent) => dirent.name + '/');
40
57
  baseUrlDirs = dirNames;
41
58
  }
42
- const cache = new Map();
43
59
  const getFile = (original, paths) => {
44
60
  if (paths.length === 0) {
45
- throw new Error('Cannot resolve import ' + original);
61
+ console.warn('Cannot resolve import ' + original);
62
+ return null;
46
63
  }
47
64
  const path = paths[0];
48
65
  try {
@@ -52,18 +69,18 @@ module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsC
52
69
  return getFile(original, paths.slice(1));
53
70
  }
54
71
  };
55
- const isPathNotANodeModule = (path) => {
72
+ const shouldPathBeAnalyzed = (path) => {
56
73
  const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(path));
57
74
  const isRelative = path.startsWith('./') || path.startsWith('../');
58
75
  const isAbsolute = path.startsWith('/');
59
76
  const isBaseUrlPath = baseUrlDirs.some((dir) => path.startsWith(dir));
60
77
  return aliasRegexIdx > -1 || isRelative || isAbsolute || isBaseUrlPath;
61
78
  };
62
- const cacheKey = (identifier, filePath) => `${identifier}-${filePath}`;
79
+ const getCacheKey = (identifier, filePath) => `${identifier}-${filePath}`;
63
80
  const lookup = (identifier, filePath, cwd) => {
64
- const cached = cache.get(cacheKey(identifier, filePath));
81
+ const cached = cache.get(getCacheKey(identifier, filePath));
65
82
  if (cached) {
66
- return cached;
83
+ return { ...cached, isCached: true };
67
84
  }
68
85
  const withExtension = /(\.ts|\.tsx)$/.test(filePath)
69
86
  ? [filePath]
@@ -77,117 +94,124 @@ module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsC
77
94
  `${filePath}/index.js`,
78
95
  `${filePath}/index.jsx`
79
96
  ];
80
- const [resolvedFilePath, file] = getFile(filePath, withExtension);
81
- const ast = parser.parse(file, {
82
- sourceType: 'module',
83
- plugins: [
84
- 'jsx',
85
- 'typescript',
86
- 'objectRestSpread',
87
- 'classProperties',
88
- 'asyncGenerators',
89
- 'decorators-legacy'
90
- ]
91
- });
92
- /**
93
- * {
94
- * identifier?: string,
95
- * source: string
96
- * }
97
- */
98
- const toLookup = [];
99
- let resolvedAs = null;
100
- ast.program.body.forEach((declaration) => {
101
- var _a, _b, _c;
102
- if (resolvedAs === null) {
103
- if (types.isExportNamedDeclaration(declaration)) {
104
- if (((_a = declaration.declaration) === null || _a === void 0 ? void 0 : _a.type.startsWith('TS')) &&
105
- ((_b = declaration.declaration) === null || _b === void 0 ? void 0 : _b.type.endsWith('Declaration'))) {
106
- const typeName = declaration.declaration.id.name;
107
- if (typeName === identifier) {
108
- resolvedAs = {
109
- // This should be 'type' of something else, but ESLint would handle that
110
- type: 'named',
111
- identifier,
112
- source: filePath
113
- };
97
+ const fileInfo = getFile(filePath, withExtension);
98
+ if (!fileInfo) {
99
+ return { resolvedAs: null, visitedFiles: [] };
100
+ }
101
+ const [resolvedFilePath, file] = fileInfo;
102
+ try {
103
+ const ast = parser.parse(file, babelParsingOptions_1.babelParsingOptions);
104
+ /**
105
+ * {
106
+ * identifier?: string,
107
+ * source: string
108
+ * }
109
+ */
110
+ const toLookup = [];
111
+ let resolvedAs = null;
112
+ ast.program.body.forEach((declaration) => {
113
+ var _a, _b, _c;
114
+ if (resolvedAs === null) {
115
+ if (types.isExportNamedDeclaration(declaration)) {
116
+ if (((_a = declaration.declaration) === null || _a === void 0 ? void 0 : _a.type.startsWith('TS')) &&
117
+ ((_b = declaration.declaration) === null || _b === void 0 ? void 0 : _b.type.endsWith('Declaration'))) {
118
+ const typeName = declaration.declaration.id.name;
119
+ if (typeName === identifier) {
120
+ resolvedAs = {
121
+ // This should be 'type' of something else, but ESLint would handle that
122
+ type: 'named',
123
+ identifier,
124
+ source: filePath
125
+ };
126
+ }
114
127
  }
115
- }
116
- else if (types.isVariableDeclaration(declaration.declaration)) {
117
- const hasIdentifier = declaration.declaration.declarations.find((declarator) => {
118
- return declarator.id.name === identifier;
119
- });
120
- if (hasIdentifier) {
121
- resolvedAs = {
122
- type: 'named',
123
- identifier,
124
- source: filePath
125
- };
128
+ else if (types.isVariableDeclaration(declaration.declaration)) {
129
+ const hasIdentifier = declaration.declaration.declarations.find((declarator) => {
130
+ return declarator.id.name === identifier;
131
+ });
132
+ if (hasIdentifier) {
133
+ resolvedAs = {
134
+ type: 'named',
135
+ identifier,
136
+ source: filePath
137
+ };
138
+ }
126
139
  }
127
- }
128
- else if (types.isFunctionDeclaration(declaration.declaration) ||
129
- types.isClassDeclaration(declaration.declaration)) {
130
- if (declaration.declaration.id.name === identifier) {
131
- resolvedAs = {
132
- type: 'named',
133
- identifier,
134
- source: filePath
135
- };
140
+ else if (types.isFunctionDeclaration(declaration.declaration) ||
141
+ types.isClassDeclaration(declaration.declaration)) {
142
+ if (declaration.declaration.id.name === identifier) {
143
+ resolvedAs = {
144
+ type: 'named',
145
+ identifier,
146
+ source: filePath
147
+ };
148
+ }
136
149
  }
137
- }
138
- else {
139
- const source = (_c = declaration.source) === null || _c === void 0 ? void 0 : _c.value;
140
- declaration.specifiers.forEach((specifier) => {
141
- if (types.isExportSpecifier(specifier)) {
142
- if (specifier.exported.name === identifier) {
143
- if (specifier.local.name === 'default' && source) {
144
- resolvedAs = {
145
- type: 'default',
146
- identifier,
147
- source: getModulePath(source, resolvedFilePath, cwd)
148
- };
149
- }
150
- else if (source === undefined) {
151
- resolvedAs = {
152
- type: 'named',
153
- identifier,
154
- source: filePath
155
- };
156
- }
157
- else if (isPathNotANodeModule(source)) {
158
- toLookup.push({
159
- identifier: specifier.local.name,
160
- source: getModulePath(source, resolvedFilePath, cwd)
161
- });
150
+ else {
151
+ const source = (_c = declaration.source) === null || _c === void 0 ? void 0 : _c.value;
152
+ declaration.specifiers.forEach((specifier) => {
153
+ if (types.isExportSpecifier(specifier)) {
154
+ if (specifier.exported.name === identifier) {
155
+ if (specifier.local.name === 'default' && source) {
156
+ resolvedAs = {
157
+ type: 'default',
158
+ identifier,
159
+ source: getModulePath(source, resolvedFilePath, cwd)
160
+ };
161
+ }
162
+ else if (source === undefined) {
163
+ // Here we could check if identifier comes from import statement, and if so, lookup deeper
164
+ resolvedAs = {
165
+ type: 'named',
166
+ identifier,
167
+ source: filePath
168
+ };
169
+ }
170
+ else if (shouldPathBeAnalyzed(source)) {
171
+ toLookup.push({
172
+ identifier: specifier.local.name,
173
+ source: getModulePath(source, resolvedFilePath, cwd)
174
+ });
175
+ }
162
176
  }
163
177
  }
164
- }
178
+ });
179
+ }
180
+ }
181
+ else if (types.isExportAllDeclaration(declaration) &&
182
+ shouldPathBeAnalyzed(declaration.source.value)) {
183
+ toLookup.push({
184
+ identifier,
185
+ source: getModulePath(declaration.source.value, resolvedFilePath, cwd)
165
186
  });
166
187
  }
167
188
  }
168
- else if (types.isExportAllDeclaration(declaration) &&
169
- isPathNotANodeModule(declaration.source.value)) {
170
- toLookup.push({
171
- identifier,
172
- source: getModulePath(declaration.source.value, resolvedFilePath, cwd)
173
- });
174
- }
189
+ });
190
+ if (resolvedAs) {
191
+ return { resolvedAs, visitedFiles: [resolvedAs.source] };
175
192
  }
176
- });
177
- if (resolvedAs) {
178
- return resolvedAs;
193
+ const nestedResult = toLookup
194
+ .map(({ identifier, source }) => lookup(identifier, source, cwd))
195
+ .filter((lookUpResult) => lookUpResult.resolvedAs !== null);
196
+ if (nestedResult[0]) {
197
+ return {
198
+ resolvedAs: nestedResult[0].resolvedAs,
199
+ visitedFiles: [resolvedFilePath, ...nestedResult[0].visitedFiles]
200
+ };
201
+ }
202
+ return { resolvedAs: null, visitedFiles: [] };
203
+ }
204
+ catch (e) {
205
+ console.log('Lookup parse error', filePath, e);
206
+ process.exit(0);
179
207
  }
180
- const nestedResult = toLookup
181
- .map(({ identifier, source }) => lookup(identifier, source, cwd))
182
- .filter(Boolean);
183
- return nestedResult[0];
184
208
  };
185
209
  const getModulePath = (sourcePath, fileName, cwd) => {
186
210
  var _a;
187
211
  const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(sourcePath));
188
212
  const relativeFileName = node_path.relative(cwd, fileName);
189
213
  const aliasKey = aliasesKeys[aliasRegexIdx];
190
- const alias = (_a = aliases[aliasKey]) === null || _a === void 0 ? void 0 : _a[0];
214
+ const alias = (_a = aliases[aliasKey]) === null || _a === void 0 ? void 0 : _a[0]; // TODO we assume that only one aliased path can exist in config
191
215
  const isAbsoluteToBaseDir = baseUrlDirs.some((baseUrlDir) => sourcePath.startsWith(baseUrlDir));
192
216
  let modulePath = '';
193
217
  if (alias) {
@@ -207,115 +231,214 @@ module.exports = function plugin({ types }, { tsConfigPath = (0, utils_1.findTsC
207
231
  }
208
232
  return modulePath;
209
233
  };
234
+ const getImportKind = (sourcePath) => {
235
+ const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(sourcePath));
236
+ const isRelative = sourcePath.startsWith('./') || sourcePath.startsWith('../');
237
+ const isBaseUrlPath = baseUrlDirs.some((dir) => sourcePath.startsWith(dir));
238
+ if (aliasRegexIdx > -1) {
239
+ return 'aliased';
240
+ }
241
+ if (isRelative) {
242
+ return 'relative';
243
+ }
244
+ if (isBaseUrlPath) {
245
+ return 'baseUrl';
246
+ }
247
+ throw new Error('Could not determine import kind');
248
+ };
210
249
  return {
211
250
  visitor: {
212
251
  Program() {
213
252
  // console.log('Cache size', cache.size)
214
253
  },
215
- ImportDeclaration(path, { filename }) {
216
- const sourceRelative = (source) => {
217
- const rel = node_path.relative(node_path.dirname(filename), source);
254
+ ImportDeclaration(path, state) {
255
+ const filename = state.filename;
256
+ const getImportSourceFormatted = (resolvedSourcePath, importKind) => {
257
+ const baseDirPath = node_path.join(root, baseUrl);
258
+ if (importKind === 'baseUrl') {
259
+ const relativeToBaseUrl = node_path.relative(baseDirPath, resolvedSourcePath);
260
+ return relativeToBaseUrl;
261
+ }
262
+ if (importKind === 'aliased') {
263
+ const originalSource = path.node.source.value;
264
+ const currentAliasIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(originalSource));
265
+ const resolvedSourcePathRelativeToBaseUrl = resolvedSourcePath
266
+ .replace(baseDirPath, '')
267
+ .replace(/^\//, '');
268
+ // Try to use current alias if it matches new path
269
+ if (currentAliasIdx > -1) {
270
+ const aliasKey = aliasesKeys[currentAliasIdx];
271
+ const aliasedPathRegExp = aliasedPathRegExps[currentAliasIdx];
272
+ if (aliasedPathRegExp.test(resolvedSourcePathRelativeToBaseUrl)) {
273
+ return interpolateAliasWithPath(aliasKey, aliasedPathRegExp, resolvedSourcePathRelativeToBaseUrl);
274
+ }
275
+ }
276
+ // Try finding matching alias
277
+ const newMatchingAliasIndex = aliasedPathRegExps.findIndex((aliasedPathRegexp) => aliasedPathRegexp.test(resolvedSourcePathRelativeToBaseUrl));
278
+ if (newMatchingAliasIndex > -1) {
279
+ const aliasKey = aliasesKeys[newMatchingAliasIndex];
280
+ const aliasedPathRegExp = aliasedPathRegExps[newMatchingAliasIndex];
281
+ return interpolateAliasWithPath(aliasKey, aliasedPathRegExp, resolvedSourcePathRelativeToBaseUrl);
282
+ }
283
+ }
284
+ const rel = node_path.relative(node_path.dirname(filename), resolvedSourcePath);
218
285
  const whatever = rel.startsWith('.') ? rel : './' + rel;
219
286
  // remove file extension
220
287
  return whatever.replace(/\.(ts|js|tsx|jsx|cjs|mjs)$/, '');
221
288
  };
222
289
  const node = path.node;
290
+ const isTypeImport = node.importKind === 'type';
223
291
  const source = node.source;
224
292
  if (source.type !== 'StringLiteral') {
225
293
  return;
226
294
  }
227
- const shouldSkip = node[SKIP] || !isPathNotANodeModule(source.value);
295
+ const shouldSkip = node[SKIP] || !shouldPathBeAnalyzed(source.value);
228
296
  if (shouldSkip) {
229
297
  return;
230
298
  }
299
+ const importKind = getImportKind(source.value);
231
300
  const modulePath = getModulePath(source.value, filename, root);
232
- const defaultSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
233
- const namespaceSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportNamespaceSpecifier');
234
- const specifiers = node.specifiers.filter((specifier) => specifier.type === 'ImportSpecifier');
301
+ const defaultSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier' // import $$ from '$$'
302
+ );
303
+ const namespaceSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportNamespaceSpecifier' // import * as $$ from '$$'
304
+ );
305
+ const specifiers = node.specifiers.filter((specifier) => specifier.type === 'ImportSpecifier' // import { $$ } from '$$'
306
+ );
235
307
  const results = specifiers.map((specifier) => {
236
308
  const importedName = specifier.imported.name;
237
309
  const result = lookup(importedName, modulePath, root);
238
- if (!result) {
239
- return {
310
+ if (!(result === null || result === void 0 ? void 0 : result.isCached)) {
311
+ const cacheKey = getCacheKey(importedName, modulePath);
312
+ // console.log('resolved not cached', cacheKey, result)
313
+ const originalImport = {
314
+ identifier: importedName,
315
+ local: specifier.local.name,
316
+ source: source.value // cannot cache non absolute path
317
+ };
318
+ const originalImportToCache = {
240
319
  identifier: importedName,
241
320
  local: specifier.local.name,
242
- source: source.value
321
+ source: modulePath
243
322
  };
323
+ const originalResolution = {
324
+ resolvedAs: originalImportToCache,
325
+ visitedFiles: []
326
+ };
327
+ if (!result.resolvedAs) {
328
+ cache.set(cacheKey, originalResolution);
329
+ return originalImport;
330
+ }
331
+ if (includeBarrelExportFiles &&
332
+ !includeBarrelExportFiles.some((fileThatHasToBeVisited) => result.visitedFiles.includes(fileThatHasToBeVisited))) {
333
+ cache.set(cacheKey, originalResolution);
334
+ return originalImport;
335
+ }
336
+ if (excludeBarrelExportFiles.some((fileThatCannotBeVisited) => result.visitedFiles.includes(fileThatCannotBeVisited))) {
337
+ cache.set(cacheKey, originalResolution);
338
+ return originalImport;
339
+ }
340
+ cache.set(cacheKey, result);
244
341
  }
245
- cache.set(cacheKey(importedName, modulePath), result);
246
342
  return {
247
- ...result,
248
- source: sourceRelative(result.source),
343
+ ...result.resolvedAs,
344
+ source: getImportSourceFormatted(result.resolvedAs.source, importKind),
249
345
  local: specifier.local.name
250
346
  };
251
347
  });
252
348
  const defaultResult = defaultSpecifier
253
349
  ? lookup('default', modulePath, root)
254
350
  : null;
255
- if (defaultResult) {
256
- cache.set(cacheKey('default', modulePath), defaultResult);
351
+ if (defaultResult && !defaultResult.isCached) {
352
+ const cacheKey = getCacheKey('default', modulePath);
353
+ const originalImportToCache = {
354
+ source: modulePath
355
+ };
356
+ const originalResolution = {
357
+ resolvedAs: originalImportToCache,
358
+ visitedFiles: []
359
+ };
360
+ if (!defaultResult.resolvedAs) {
361
+ cache.set(cacheKey, originalResolution);
362
+ }
363
+ else if (includeBarrelExportFiles &&
364
+ !includeBarrelExportFiles.some((fileThatHasToBeVisited) => defaultResult.visitedFiles.includes(fileThatHasToBeVisited))) {
365
+ cache.set(cacheKey, originalResolution);
366
+ }
367
+ else if (excludeBarrelExportFiles.some((fileThatCannotBeVisited) => defaultResult.visitedFiles.includes(fileThatCannotBeVisited))) {
368
+ cache.set(cacheKey, originalResolution);
369
+ }
370
+ else {
371
+ cache.set(cacheKey, defaultResult);
372
+ }
257
373
  }
258
- const buildNamed = template(`
259
- import { %%IMPORT_NAME%% } from %%SOURCE%%;
374
+ const buildNamed = (0, template_1.template)(`
375
+ import { %%IMPORT_NAME%% } from '%%SOURCE%%';
260
376
  `);
261
- const buildNamedWithAlias = template(`
262
- import { %%IMPORTED_NAME%% as %%LOCAL_NAME%% } from %%SOURCE%%;
377
+ const buildNamedWithAlias = (0, template_1.template)(`
378
+ import { %%IMPORTED_NAME%% as %%LOCAL_NAME%% } from '%%SOURCE%%';
263
379
  `);
264
- const buildDefault = template(`
265
- import %%IMPORT_NAME%% from %%SOURCE%%;
380
+ const buildDefault = (0, template_1.template)(`
381
+ import %%IMPORT_NAME%% from '%%SOURCE%%';
266
382
  `);
267
- const buildNamespace = template(`
268
- import * as %%IMPORT_NAME%% from %%SOURCE%%;
383
+ const buildNamespace = (0, template_1.template)(`
384
+ import * as %%IMPORT_NAME%% from '%%SOURCE%%';
269
385
  `);
270
- const defaultImport = defaultResult
386
+ const defaultImport = (defaultResult === null || defaultResult === void 0 ? void 0 : defaultResult.resolvedAs)
271
387
  ? [
272
388
  buildDefault({
273
- IMPORT_NAME: types.identifier(defaultSpecifier.local.name),
274
- SOURCE: types.stringLiteral(sourceRelative(defaultResult.source))
389
+ IMPORT_NAME: defaultSpecifier.local.name,
390
+ SOURCE: getImportSourceFormatted(defaultResult.resolvedAs.source, importKind)
275
391
  })
276
392
  ]
277
393
  : defaultSpecifier
278
394
  ? [
279
395
  buildDefault({
280
- IMPORT_NAME: types.identifier(defaultSpecifier.local.name),
281
- SOURCE: types.stringLiteral(source.value)
396
+ IMPORT_NAME: defaultSpecifier.local.name,
397
+ SOURCE: source.value
282
398
  })
283
399
  ]
284
400
  : [];
285
401
  const namespaceImport = namespaceSpecifier
286
402
  ? [
287
403
  buildNamespace({
288
- IMPORT_NAME: types.identifier(namespaceSpecifier.local.name),
289
- SOURCE: types.stringLiteral(source.value)
404
+ IMPORT_NAME: namespaceSpecifier.local.name,
405
+ SOURCE: source.value
290
406
  })
291
407
  ]
292
408
  : [];
293
- const named = results.map(({ type, identifier, local, source }) => {
294
- if (type === 'default') {
295
- return buildDefault({
296
- IMPORT_NAME: types.identifier(identifier),
297
- SOURCE: types.stringLiteral(source)
298
- });
299
- }
300
- else if (identifier !== local) {
301
- return buildNamedWithAlias({
302
- IMPORTED_NAME: types.identifier(identifier),
303
- LOCAL_NAME: types.identifier(local),
304
- SOURCE: types.stringLiteral(source)
305
- });
306
- }
307
- else {
308
- return buildNamed({
309
- IMPORT_NAME: types.identifier(identifier),
310
- SOURCE: types.stringLiteral(source)
311
- });
312
- }
409
+ const importsFromNamedGroupedBySource = Object.values((0, groupBy_1.groupBy)(results, 'source'));
410
+ const named = importsFromNamedGroupedBySource.map((imports) => {
411
+ const source = imports[0].source;
412
+ const defaultImport = imports.find(({ type }) => type === 'default');
413
+ const nonDefault = imports.filter(({ type }) => type !== 'default');
414
+ const defaultPart = defaultImport
415
+ ? `${defaultImport.identifier}`
416
+ : null;
417
+ const nonDefaultPart = nonDefault.length > 0
418
+ ? nonDefault
419
+ .map(({ identifier, local }) => identifier !== local
420
+ ? `${identifier} as ${local}`
421
+ : identifier)
422
+ .join(', ')
423
+ : null;
424
+ return `import ${isTypeImport ? 'type ' : ''}${defaultPart ? `${defaultPart}${nonDefaultPart ? ', ' : ''}` : ''}${nonDefaultPart ? `{ ${nonDefaultPart} }` : ''} from '${source}';`;
313
425
  });
314
426
  const newImports = [...namespaceImport, ...defaultImport, ...named].map((node) => {
315
- node[SKIP] = true;
316
427
  return node;
317
428
  });
318
- path.replaceWithMultiple(newImports);
429
+ if (!state.file.metadata) {
430
+ state.file.metadata = {};
431
+ }
432
+ if (!state.file.metadata[filename]) {
433
+ state.file.metadata[filename] = [];
434
+ }
435
+ const modification = {
436
+ modificationCode: newImports.join('\n'),
437
+ start: path.node.start,
438
+ end: path.node.end,
439
+ loc: path.node.loc
440
+ };
441
+ state.file.metadata[filename].push(modification);
319
442
  }
320
443
  }
321
444
  };
@@ -0,0 +1,30 @@
1
+ export declare type CodeModification = {
2
+ modificationCode: string;
3
+ };
4
+ export declare type CodeChange = CodeModification;
5
+ export declare type CodeChangeWithLocation = CodeModification & MatchPosition;
6
+ export declare type MatchPosition = {
7
+ start: number;
8
+ end: number;
9
+ loc: Location;
10
+ };
11
+ declare type ModifyCodeAsTextParams = {
12
+ code: string;
13
+ modificationCode?: string;
14
+ alreadyChangedCodes?: string[];
15
+ location: Pick<MatchPosition, 'start' | 'end'>;
16
+ };
17
+ declare type ProcessCodeModificationsArrayParams = {
18
+ code: string;
19
+ changes: CodeChangeWithLocation[];
20
+ };
21
+ export declare const regExpTest: (regExp: RegExp, text: string) => boolean;
22
+ export declare function modifyCodeAsText({ code, modificationCode, location }: ModifyCodeAsTextParams): {
23
+ fileCode: string;
24
+ locationsChange: {
25
+ from: number;
26
+ to: number;
27
+ };
28
+ };
29
+ export declare function processTextCodeModificationsArray({ code, changes }: ProcessCodeModificationsArrayParams): string;
30
+ export {};
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processTextCodeModificationsArray = exports.modifyCodeAsText = exports.regExpTest = void 0;
4
+ const regExpTest = (regExp, text) => {
5
+ if (!text) {
6
+ return false;
7
+ }
8
+ const matches = text.match(regExp);
9
+ return matches !== null && matches.length > 0;
10
+ };
11
+ exports.regExpTest = regExpTest;
12
+ function modifyCodeAsText({ code, modificationCode, location }) {
13
+ let fileCode = code;
14
+ const codeBeforeMatch = fileCode.slice(0, location.start);
15
+ const codeAfterMatch = fileCode.slice(location.end);
16
+ const replacedCodeLength = location.end - location.start;
17
+ const replacementCodeLength = modificationCode.length;
18
+ const locationsChange = {
19
+ from: location.end,
20
+ to: location.end + replacementCodeLength - replacedCodeLength
21
+ };
22
+ fileCode = `${codeBeforeMatch}${modificationCode}${codeAfterMatch}`;
23
+ return { fileCode, locationsChange };
24
+ }
25
+ exports.modifyCodeAsText = modifyCodeAsText;
26
+ function processTextCodeModificationsArray({ code, changes }) {
27
+ let modifiedCode = code;
28
+ /**
29
+ * Include only changes that are unique by it's location.
30
+ * Remove changes that are inside range of other changes
31
+ */
32
+ const pendingChanges = changes.filter((change, changeIdx) => !changes.some((otherChange, otherChangeIdx) => otherChangeIdx !== changeIdx &&
33
+ otherChange.start <= change.start &&
34
+ otherChange.end >= change.end &&
35
+ // insert changes has the same start and end to distinguish them from anchor node, that might have other changes attached
36
+ change.start !== change.end));
37
+ while (pendingChanges.length > 0) {
38
+ const change = pendingChanges.shift();
39
+ const { locationsChange, fileCode } = modifyCodeAsText({
40
+ code: modifiedCode,
41
+ modificationCode: change.modificationCode,
42
+ location: { start: change.start, end: change.end }
43
+ });
44
+ modifiedCode = fileCode;
45
+ pendingChanges.forEach((pendingChange) => {
46
+ if (pendingChange.start >= locationsChange.from) {
47
+ const diff = locationsChange.to - locationsChange.from;
48
+ pendingChange.end += diff;
49
+ pendingChange.start += diff;
50
+ }
51
+ });
52
+ }
53
+ return modifiedCode;
54
+ }
55
+ exports.processTextCodeModificationsArray = processTextCodeModificationsArray;
@@ -0,0 +1 @@
1
+ export declare function template(template: string): (params: Record<string, string>) => string;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.template = void 0;
4
+ function template(template) {
5
+ return (params) => {
6
+ let code = template.trim();
7
+ Object.entries(params).forEach(([key, value]) => {
8
+ code = code.replace(new RegExp(`%%${key}%%`, 'g'), value);
9
+ });
10
+ return code;
11
+ };
12
+ }
13
+ exports.template = template;
@@ -1 +1,6 @@
1
- export {};
1
+ export function transform({ rootPath, inputFilePath, includeBarrelExportFiles, excludeBarrelExportFiles }: {
2
+ rootPath: any;
3
+ inputFilePath: any;
4
+ includeBarrelExportFiles: any;
5
+ excludeBarrelExportFiles: any;
6
+ }): Promise<void>;
@@ -1,17 +1,15 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transform = void 0;
2
4
  /*eslint-disable @typescript-eslint/no-var-requires */
3
5
  const { getFilesList } = require('@codeque/core');
4
6
  const babelCore = require('@babel/core');
7
+ const parser = require('@babel/parser');
5
8
  const fs = require('fs');
6
9
  const path = require('path');
7
- const rootPath = process.argv[2];
8
- const inputFilePath = process.argv[3];
9
- if (!rootPath) {
10
- console.error('Please provide correct transformation root');
11
- process.exit(1);
12
- }
13
- ;
14
- (async () => {
10
+ const babelParsingOptions_1 = require("./babelParsingOptions");
11
+ const processCodeTextModificationsArray_1 = require("./processCodeTextModificationsArray");
12
+ const transform = async ({ rootPath, inputFilePath, includeBarrelExportFiles, excludeBarrelExportFiles }) => {
15
13
  const root = path.resolve(rootPath);
16
14
  const resolvedInputFilePath = inputFilePath
17
15
  ? path.join(root, inputFilePath)
@@ -25,19 +23,34 @@ if (!rootPath) {
25
23
  });
26
24
  const errors = [];
27
25
  let progressCount = 0;
26
+ let cache = new Map();
28
27
  for (const filePath of filesList) {
29
28
  try {
30
29
  const fileName = path.parse(filePath).name;
30
+ const fileContent = fs.readFileSync(filePath).toString();
31
31
  const result = babelCore.transformFileSync(filePath, {
32
32
  plugins: [
33
- ['./babel.js', { tsConfigPath: path.join(root, 'tsconfig.json') }]
33
+ [
34
+ __dirname + '/index.js',
35
+ {
36
+ tsConfigPath: path.join(root, 'tsconfig.json'),
37
+ cache,
38
+ includeBarrelExportFiles,
39
+ excludeBarrelExportFiles
40
+ }
41
+ ]
34
42
  ],
35
- parserOpts: {
36
- plugins: ['typescript', 'jsx']
37
- },
43
+ parserOpts: babelParsingOptions_1.babelParsingOptions,
38
44
  filename: fileName
39
45
  });
40
- fs.writeFileSync(filePath, result.code);
46
+ const changes = result.metadata[filePath];
47
+ if ((changes === null || changes === void 0 ? void 0 : changes.length) > 0) {
48
+ const resultCode = (0, processCodeTextModificationsArray_1.processTextCodeModificationsArray)({
49
+ code: fileContent,
50
+ changes
51
+ });
52
+ fs.writeFileSync(filePath, resultCode);
53
+ }
41
54
  progressCount++;
42
55
  if (progressCount % 100 === 0) {
43
56
  console.log(`${progressCount}+${errors.length}/${filesList.length}`);
@@ -49,4 +62,5 @@ if (!rootPath) {
49
62
  }
50
63
  console.log(errors);
51
64
  console.log(`Done: ${progressCount}/${filesList.length}; Failed: ${errors.length}`);
52
- })();
65
+ };
66
+ exports.transform = transform;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const rootPath = process.argv[2];
4
+ const inputFilePath = process.argv[3];
5
+ const transform_1 = require("./transform");
6
+ if (!rootPath) {
7
+ console.error('Please provide correct transformation root');
8
+ process.exit(1);
9
+ }
10
+ const run = async () => {
11
+ const startTime = new Date().getTime();
12
+ await (0, transform_1.transform)({
13
+ rootPath,
14
+ inputFilePath
15
+ });
16
+ console.log('Operation time: ', (new Date().getTime() - startTime) / 1000);
17
+ };
18
+ run();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rev-dep",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "Dependency debugging tool for JavaScript and TypeScript projects",
5
5
  "main": "dist/module.js",
6
6
  "bin": "bin.js",