@putout/plugin-esm 4.1.2 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,10 @@ npm i putout @putout/plugin-esm -D
27
27
  - ✅ [remove-empty-import](#remove-empty-import);
28
28
  - ✅ [remove-empty-export](#remove-empty-export);
29
29
  - ✅ [sort-imports-by-specifiers](#sort-imports-by-specifiers);
30
+
31
+ ## File rules
32
+
33
+ - ✅ [apply-namespace-import-file](#resolve-imported-file);
30
34
  - ✅ [resolve-imported-file](#resolve-imported-file);
31
35
 
32
36
  ## Config
@@ -306,6 +310,44 @@ import('foo.json', {
306
310
  });
307
311
  ```
308
312
 
313
+ ## File Rules
314
+
315
+ ### apply-namespace-import-to-file
316
+
317
+ Check out in 🐊**Putout Editor**:
318
+
319
+ - ✅ [`apply-namespace-import-to-file`](https://putout.cloudcmd.io/#/gist/1492d584559e5798325047de679222a0/c6a37a803b80823de1b64ab944f2427aecefb51b);
320
+ - ✅ [`get-imports`](https://putout.cloudcmd.io/#/gist/5d7687215e9fbdf705935c444503dded/75a98d2db9d3847c73017e41637924b1cfd5a598);
321
+ - ✅ [`has-export-default`](https://putout.cloudcmd.io/#/gist/b50ccfe5cc8c0c97e2fc98b37903ade4/fbc026e6f1027581f7aa4879dcafcaa7754bf8f4);
322
+ - ✅ [`apply-namespace-import`](https://putout.cloudcmd.io/#/gist/23a6dc6741b772c03fbed95feda2b451/1fbecac6fc40282bcda0593aa666a8c213ef85b7);
323
+
324
+ Let's consider file structure:
325
+
326
+ ```
327
+ /
328
+ |-- lib/
329
+ | `-- index.js "import a from './a.js';"
330
+ | `-- a.js "export const x = 2;"
331
+ ```
332
+
333
+ In this case `index.js` can be fixed:
334
+
335
+ #### ❌ Example of incorrect code
336
+
337
+ ```js
338
+ import a from './a.js';
339
+ ```
340
+
341
+ #### ✅ Example of correct code
342
+
343
+ ```js
344
+ import * as a from './a.js';
345
+ ```
346
+
347
+ ## License
348
+
349
+ MIT
350
+
309
351
  ### resolve-imported-file
310
352
 
311
353
  Check out in 🐊**Putout Editor**:
@@ -0,0 +1,16 @@
1
+ export const report = (path) => {
2
+ const source = path.node.source.value;
3
+ const {specifiers} = path.node;
4
+ const [{local}] = specifiers;
5
+ const {name} = local;
6
+
7
+ return `'import ${name} from "${source}"' -> 'import * as ${name} from "${source}"'`;
8
+ };
9
+
10
+ export const replace = ({options}) => {
11
+ const {name, source} = options;
12
+
13
+ return {
14
+ [`import ${name} from "${source}"`]: `import * as ${name} from "${source}"`,
15
+ };
16
+ };
@@ -0,0 +1,29 @@
1
+ export const include = () => [
2
+ 'import __a from "__b"',
3
+ ];
4
+
5
+ export const report = (path) => {
6
+ const [name, source] = getImport(path);
7
+ return `${name} <- ${source}`;
8
+ };
9
+
10
+ export const fix = (path) => {
11
+ const [name, source] = getImport(path);
12
+
13
+ path.node.leadingComments = [
14
+ CommentLine(`${name} <- ${source}'`),
15
+ ];
16
+ };
17
+
18
+ const CommentLine = (value) => ({
19
+ type: 'CommentLine',
20
+ value: ` ${value}`,
21
+ });
22
+
23
+ const getImport = (path) => {
24
+ const source = path.node.source.value;
25
+ const [first] = path.node.specifiers;
26
+ const {name} = first.local;
27
+
28
+ return [name, source];
29
+ };
@@ -0,0 +1,6 @@
1
+ export const report = (path) => path.node.type;
2
+
3
+ export const fix = () => {};
4
+ export const include = () => [
5
+ 'ExportDefaultDeclaration',
6
+ ];
@@ -0,0 +1,123 @@
1
+ import {join, dirname} from 'node:path';
2
+ import putout, {
3
+ parse,
4
+ print,
5
+ transform,
6
+ operator,
7
+ } from 'putout';
8
+ import * as isESMPlugin from './is-esm/index.js';
9
+ import * as hasExportDefaultPlugin from './has-export-default/index.js';
10
+ import * as applyNamespaceImportPlugin from './apply-namespace-import/index.js';
11
+ import * as getImportsPlugin from './get-imports/index.js';
12
+
13
+ const {
14
+ findFile,
15
+ getFilename,
16
+ readFileContent,
17
+ writeFileContent,
18
+ } = operator;
19
+
20
+ const getMessage = (a) => a.message;
21
+ const isESM = (a) => a.rule === 'is-esm';
22
+ const hasExportDefault = (a) => a.rule === 'has-export-default';
23
+
24
+ export const report = (file, {name, source}) => `Use 'import * as ${name} from '${source}'`;
25
+ export const fix = (file, {name, source, content, ast}) => {
26
+ transform(ast, content, {
27
+ rules: {
28
+ 'apply-namespace-import': ['on', {
29
+ name,
30
+ source,
31
+ }],
32
+ },
33
+ plugins: [
34
+ ['apply-namespace-import', applyNamespaceImportPlugin],
35
+ ],
36
+ });
37
+
38
+ const newContent = print(ast);
39
+
40
+ writeFileContent(file, newContent);
41
+ };
42
+
43
+ export const scan = (rootPath, {push, trackFile}) => {
44
+ const mask = [
45
+ '*.js',
46
+ '*.mjs',
47
+ ];
48
+
49
+ for (const file of trackFile(rootPath, mask)) {
50
+ const content = readFileContent(file);
51
+ const ast = parse(content);
52
+ const importsTuples = getImports(file, content, ast);
53
+
54
+ for (const [name, source, importedFilename] of importsTuples) {
55
+ if (hasImportDefault(rootPath, importedFilename))
56
+ continue;
57
+
58
+ push(file, {
59
+ name,
60
+ source,
61
+ ast,
62
+ content,
63
+ });
64
+ }
65
+ }
66
+ };
67
+
68
+ function getImports(file, content, ast) {
69
+ if (!content.includes('import'))
70
+ return [];
71
+
72
+ const places = transform(ast, content, {
73
+ fix: false,
74
+ plugins: [
75
+ ['get-imports', getImportsPlugin],
76
+ ],
77
+ });
78
+
79
+ const filename = getFilename(file);
80
+ const dir = dirname(filename);
81
+
82
+ const imports = places.map(getMessage);
83
+
84
+ return buildImports(dir, imports);
85
+ }
86
+
87
+ function hasImportDefault(rootPath, importedFilename) {
88
+ const [importedFile] = findFile(rootPath, importedFilename);
89
+
90
+ if (!importedFile)
91
+ return true;
92
+
93
+ const importedContent = readFileContent(importedFile);
94
+
95
+ const {places} = putout(importedContent, {
96
+ fix: false,
97
+ plugins: [
98
+ ['has-export-default', hasExportDefaultPlugin],
99
+ ['is-esm', isESMPlugin],
100
+ ],
101
+ });
102
+
103
+ const esm = places.filter(isESM);
104
+ const defaultExport = places.filter(hasExportDefault);
105
+
106
+ if (defaultExport.length)
107
+ return true;
108
+
109
+ return !esm.length;
110
+ }
111
+
112
+ function buildImports(dir, imports) {
113
+ const list = [];
114
+
115
+ for (const current of imports) {
116
+ const [name, source] = current.split(' <- ');
117
+ const full = join(dir, source);
118
+
119
+ list.push([name, source, full]);
120
+ }
121
+
122
+ return list;
123
+ }
@@ -0,0 +1,18 @@
1
+ export const report = (path) => path.type;
2
+
3
+ export const fix = (path) => {
4
+ path.node.leadingComments = [
5
+ CommentLine('esm'),
6
+ ];
7
+ };
8
+
9
+ export const include = () => [
10
+ 'ExportDefaultDeclaration',
11
+ 'ExportNamedDeclaration',
12
+ 'ImportDeclaration',
13
+ ];
14
+
15
+ const CommentLine = (value) => ({
16
+ type: 'CommentLine',
17
+ value: ` ${value}`,
18
+ });
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import * as applyNamespaceImportToFile from './apply-namespace-import-to-file/index.js';
1
2
  import * as resolveImportedFile from './resolve-imported-file/index.js';
2
3
  import * as addIndexToImport from './add-index-to-import/index.js';
3
4
  import * as declareImportsFirst from './declare-imports-first/index.js';
@@ -22,4 +23,5 @@ export const rules = {
22
23
  'remove-empty-export': removeEmptyExport,
23
24
  'sort-imports-by-specifiers': sortImportsBySpecifiers,
24
25
  'resolve-imported-file': ['off', resolveImportedFile],
26
+ 'apply-namespace-import-to-file': ['off', applyNamespaceImportToFile],
25
27
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/plugin-esm",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "type": "module",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "🐊Putout plugin improves ability to transform ESM code",