@putout/plugin-esm 8.0.2 → 8.1.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
|
@@ -34,7 +34,8 @@ npm i putout @putout/plugin-esm -D
|
|
|
34
34
|
|
|
35
35
|
## File rules
|
|
36
36
|
|
|
37
|
-
- ✅ [apply-namespace-import-file](#
|
|
37
|
+
- ✅ [apply-namespace-import-to-file](#apply-namespace-import-file]);
|
|
38
|
+
- ✅ [apply-named-import-to-file](#apply-named-import-to-file);
|
|
38
39
|
- ✅ [apply-privately-imported-file](#apply-privately-imported-file);
|
|
39
40
|
- ✅ [apply-js-imported-file](#apply-js-imported-file);
|
|
40
41
|
- ✅ [resolve-imported-file](#resolve-imported-file);
|
|
@@ -61,7 +62,8 @@ npm i putout @putout/plugin-esm -D
|
|
|
61
62
|
"esm/apply-js-imported-file": "off",
|
|
62
63
|
"esm/resolve-imported-file": "off",
|
|
63
64
|
"esm/shorten-imported-file": "off",
|
|
64
|
-
"esm/apply-namespace-
|
|
65
|
+
"esm/apply-namespace-import-to-file": "off",
|
|
66
|
+
"esm/apply-named-import-to-file": "off",
|
|
65
67
|
"esm/apply-privately-imported-file": "off",
|
|
66
68
|
"esm/remove-useless-export-specifiers": "off"
|
|
67
69
|
}
|
|
@@ -399,6 +401,41 @@ import('foo.json', {
|
|
|
399
401
|
|
|
400
402
|
## File Rules
|
|
401
403
|
|
|
404
|
+
### apply-named-import-to-file
|
|
405
|
+
|
|
406
|
+
The rule fixes:
|
|
407
|
+
|
|
408
|
+
> `SyntaxError: The requested module './a.js' does not provide an export named 'default'`
|
|
409
|
+
|
|
410
|
+
Check out in 🐊**Putout Editor**:
|
|
411
|
+
|
|
412
|
+
- ✅ [`get-imports`](https://putout.cloudcmd.io/#/gist/5d7687215e9fbdf705935c444503dded/75a98d2db9d3847c73017e41637924b1cfd5a598);
|
|
413
|
+
- ✅ [`has-export-default`](https://putout.cloudcmd.io/#/gist/b50ccfe5cc8c0c97e2fc98b37903ade4/fbc026e6f1027581f7aa4879dcafcaa7754bf8f4);
|
|
414
|
+
- ✅ [`is-esm`](https://putout.cloudcmd.io/#/gist/fa080be2bf3a6560e289d84b5873c2bc/2601091f6bf97148843767968c3afcb36dde31de);
|
|
415
|
+
|
|
416
|
+
Let's consider file structure:
|
|
417
|
+
|
|
418
|
+
```
|
|
419
|
+
/
|
|
420
|
+
|-- lib/
|
|
421
|
+
| `-- index.js "import a from './a.js';"
|
|
422
|
+
| `-- a.js "export const a = 2;"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
In this case `index.js` can be fixed:
|
|
426
|
+
|
|
427
|
+
#### ❌ Example of incorrect code
|
|
428
|
+
|
|
429
|
+
```js
|
|
430
|
+
import a from './a.js';
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
#### ✅ Example of correct code
|
|
434
|
+
|
|
435
|
+
```js
|
|
436
|
+
import {a} from './a.js';
|
|
437
|
+
```
|
|
438
|
+
|
|
402
439
|
### apply-namespace-import-to-file
|
|
403
440
|
|
|
404
441
|
The rule fixes:
|
|
@@ -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 {${name}} from "${source}"'`;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const replace = ({options}) => {
|
|
11
|
+
const {name, source} = options;
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
[`import ${name} from "${source}"`]: `import {${name}} from "${source}"`,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {join, dirname} from 'node:path';
|
|
2
|
+
import {tryCatch} from 'try-catch';
|
|
3
|
+
import putout, {
|
|
4
|
+
parse,
|
|
5
|
+
print,
|
|
6
|
+
transform,
|
|
7
|
+
operator,
|
|
8
|
+
} from 'putout';
|
|
9
|
+
import {createGetPrivateImports} from '#private-imports';
|
|
10
|
+
import * as isESMPlugin from '#is-esm';
|
|
11
|
+
import * as hasExportDefaultPlugin from '#has-export-default';
|
|
12
|
+
import * as getImportsPlugin from '#get-default-imports';
|
|
13
|
+
import * as applyNamedImportPlugin from './apply-named-import/index.js';
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
findFile,
|
|
17
|
+
getFilename,
|
|
18
|
+
readFileContent,
|
|
19
|
+
writeFileContent,
|
|
20
|
+
} = operator;
|
|
21
|
+
|
|
22
|
+
const getMessage = (a) => a.message;
|
|
23
|
+
const isESM = (a) => a.rule === 'is-esm';
|
|
24
|
+
const hasExportDefault = (a) => a.rule === 'has-export-default';
|
|
25
|
+
|
|
26
|
+
export const report = (file, {name, source}) => {
|
|
27
|
+
const filename = getFilename(file);
|
|
28
|
+
return `Use 'import {${name}} from '${source}' in '${filename}'`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const fix = (file, {name, source, content, ast}) => {
|
|
32
|
+
transform(ast, content, {
|
|
33
|
+
rules: {
|
|
34
|
+
'apply-named-import': ['on', {
|
|
35
|
+
name,
|
|
36
|
+
source,
|
|
37
|
+
}],
|
|
38
|
+
},
|
|
39
|
+
plugins: [
|
|
40
|
+
['apply-named-import', applyNamedImportPlugin],
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const newContent = print(ast);
|
|
45
|
+
|
|
46
|
+
writeFileContent(file, newContent);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const scan = (rootPath, {push, trackFile}) => {
|
|
50
|
+
const mask = [
|
|
51
|
+
'*.js',
|
|
52
|
+
'*.mjs',
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const getPrivateImports = createGetPrivateImports();
|
|
56
|
+
|
|
57
|
+
for (const file of trackFile(rootPath, mask)) {
|
|
58
|
+
const content = readFileContent(file);
|
|
59
|
+
const [error, ast] = tryCatch(parse, content);
|
|
60
|
+
|
|
61
|
+
if (error)
|
|
62
|
+
continue;
|
|
63
|
+
|
|
64
|
+
const importsTuples = getImports(file, content, ast);
|
|
65
|
+
|
|
66
|
+
const privateImports = getPrivateImports(file, {
|
|
67
|
+
aliasBased: true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
for (const [name, source, importedFilename] of importsTuples) {
|
|
71
|
+
const is = hasNamedImport({
|
|
72
|
+
name,
|
|
73
|
+
rootPath,
|
|
74
|
+
importedFilename,
|
|
75
|
+
privateImports,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!is)
|
|
79
|
+
continue;
|
|
80
|
+
|
|
81
|
+
push(file, {
|
|
82
|
+
name,
|
|
83
|
+
source,
|
|
84
|
+
ast,
|
|
85
|
+
content,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
function getImports(file, content, ast) {
|
|
92
|
+
if (!content.includes('import'))
|
|
93
|
+
return [];
|
|
94
|
+
|
|
95
|
+
const places = transform(ast, content, {
|
|
96
|
+
fix: false,
|
|
97
|
+
plugins: [
|
|
98
|
+
['get-imports', getImportsPlugin],
|
|
99
|
+
],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const filename = getFilename(file);
|
|
103
|
+
const dir = dirname(filename);
|
|
104
|
+
|
|
105
|
+
const imports = places.map(getMessage);
|
|
106
|
+
|
|
107
|
+
return buildImports(dir, imports);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseImportedFilename({importedFilename, privateImports}) {
|
|
111
|
+
if (privateImports.has(importedFilename))
|
|
112
|
+
return privateImports.get(importedFilename);
|
|
113
|
+
|
|
114
|
+
return importedFilename;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function hasNamedImport({name, rootPath, importedFilename, privateImports}) {
|
|
118
|
+
const parsedName = parseImportedFilename({
|
|
119
|
+
importedFilename,
|
|
120
|
+
privateImports,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const [importedFile] = findFile(rootPath, parsedName);
|
|
124
|
+
|
|
125
|
+
if (!importedFile)
|
|
126
|
+
return false;
|
|
127
|
+
|
|
128
|
+
const importedContent = readFileContent(importedFile);
|
|
129
|
+
|
|
130
|
+
const {places} = putout(importedContent, {
|
|
131
|
+
fix: false,
|
|
132
|
+
plugins: [
|
|
133
|
+
['has-export-default', hasExportDefaultPlugin],
|
|
134
|
+
['is-esm', isESMPlugin],
|
|
135
|
+
],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const esm = places.filter(isESM);
|
|
139
|
+
|
|
140
|
+
if (!esm.length)
|
|
141
|
+
return false;
|
|
142
|
+
|
|
143
|
+
const defaultExport = places.filter(hasExportDefault);
|
|
144
|
+
|
|
145
|
+
if (defaultExport.length)
|
|
146
|
+
return false;
|
|
147
|
+
|
|
148
|
+
for (const {message} of esm) {
|
|
149
|
+
const [, exportName] = message.split(':');
|
|
150
|
+
|
|
151
|
+
if (name === exportName)
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseFull(dir, source) {
|
|
159
|
+
if (source.startsWith('#'))
|
|
160
|
+
return source;
|
|
161
|
+
|
|
162
|
+
return join(dir, source);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildImports(dir, imports) {
|
|
166
|
+
const list = [];
|
|
167
|
+
|
|
168
|
+
for (const current of imports) {
|
|
169
|
+
const [name, source] = current.split(' <- ');
|
|
170
|
+
const full = parseFull(dir, source);
|
|
171
|
+
|
|
172
|
+
list.push([name, source, full]);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return list;
|
|
176
|
+
}
|
|
@@ -7,10 +7,10 @@ import putout, {
|
|
|
7
7
|
operator,
|
|
8
8
|
} from 'putout';
|
|
9
9
|
import {createGetPrivateImports} from '#private-imports';
|
|
10
|
-
import * as isESMPlugin from '
|
|
11
|
-
import * as hasExportDefaultPlugin from '
|
|
10
|
+
import * as isESMPlugin from '#is-esm';
|
|
11
|
+
import * as hasExportDefaultPlugin from '#has-export-default';
|
|
12
|
+
import * as getImportsPlugin from '#get-default-imports';
|
|
12
13
|
import * as applyNamespaceImportPlugin from './apply-namespace-import/index.js';
|
|
13
|
-
import * as getImportsPlugin from './get-imports/index.js';
|
|
14
14
|
|
|
15
15
|
const {
|
|
16
16
|
findFile,
|
package/lib/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import * as removeEmptyExport from './remove-empty-export/index.js';
|
|
|
13
13
|
import * as mergeDuplicateImports from './merge-duplicate-imports/index.js';
|
|
14
14
|
import * as convertAssertToWith from './convert-assert-to-with/index.js';
|
|
15
15
|
import * as applyNamespaceImportToFile from './apply-namespace-import-to-file/index.js';
|
|
16
|
+
import * as applyNamedImportToFile from './apply-named-import-to-file/index.js';
|
|
16
17
|
import * as applyPrivatelyImportedFile from './apply-privately-imported-file/index.js';
|
|
17
18
|
import * as resolveImportedFile from './resolve-imported-file/index.js';
|
|
18
19
|
import * as shortenImportedFile from './shorten-imported-file/index.js';
|
|
@@ -34,6 +35,7 @@ export const rules = {
|
|
|
34
35
|
|
|
35
36
|
'resolve-imported-file': ['off', resolveImportedFile],
|
|
36
37
|
'shorten-imported-file': ['off', shortenImportedFile],
|
|
38
|
+
'apply-named-import-to-file': ['off', applyNamedImportToFile],
|
|
37
39
|
'apply-namespace-import-to-file': ['off', applyNamespaceImportToFile],
|
|
38
40
|
'apply-privately-imported-file': ['off', applyPrivatelyImportedFile],
|
|
39
41
|
'apply-js-imported-file': ['off', applyJsImportedFile],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@putout/plugin-esm",
|
|
3
|
-
"version": "8.0
|
|
3
|
+
"version": "8.1.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",
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
},
|
|
13
13
|
"imports": {
|
|
14
14
|
"#get-imports": "./lib/shorten-imported-file/get-imports/index.js",
|
|
15
|
+
"#get-default-imports": "./lib/apply-namespace-import-to-file/get-imports/index.js",
|
|
15
16
|
"#change-imports": "./lib/resolve-imported-file/change-imports/index.js",
|
|
16
|
-
"#private-imports": "./lib/apply-privately-imported-file/private-imports.js"
|
|
17
|
+
"#private-imports": "./lib/apply-privately-imported-file/private-imports.js",
|
|
18
|
+
"#is-esm": "./lib/apply-namespace-import-to-file/is-esm/index.js",
|
|
19
|
+
"#has-export-default": "./lib/apply-namespace-import-to-file/has-export-default/index.js"
|
|
17
20
|
},
|
|
18
21
|
"release": false,
|
|
19
22
|
"tag": false,
|