@putout/plugin-esm 8.0.1 → 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 +39 -2
- package/lib/apply-named-import-to-file/apply-named-import/index.js +16 -0
- package/lib/apply-named-import-to-file/index.js +176 -0
- package/lib/apply-namespace-import-to-file/index.js +12 -4
- package/lib/apply-namespace-import-to-file/is-esm/index.js +34 -1
- package/lib/index.js +2 -0
- package/package.json +5 -2
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,
|
|
@@ -69,6 +69,7 @@ export const scan = (rootPath, {push, trackFile}) => {
|
|
|
69
69
|
|
|
70
70
|
for (const [name, source, importedFilename] of importsTuples) {
|
|
71
71
|
const is = hasImportDefault({
|
|
72
|
+
name,
|
|
72
73
|
rootPath,
|
|
73
74
|
importedFilename,
|
|
74
75
|
privateImports,
|
|
@@ -113,7 +114,7 @@ function parseImportedFilename({importedFilename, privateImports}) {
|
|
|
113
114
|
return importedFilename;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
function hasImportDefault({rootPath, importedFilename, privateImports}) {
|
|
117
|
+
function hasImportDefault({name, rootPath, importedFilename, privateImports}) {
|
|
117
118
|
const parsedName = parseImportedFilename({
|
|
118
119
|
importedFilename,
|
|
119
120
|
privateImports,
|
|
@@ -140,6 +141,13 @@ function hasImportDefault({rootPath, importedFilename, privateImports}) {
|
|
|
140
141
|
if (defaultExport.length)
|
|
141
142
|
return true;
|
|
142
143
|
|
|
144
|
+
for (const {message} of esm) {
|
|
145
|
+
const [, exportName] = message.split(':');
|
|
146
|
+
|
|
147
|
+
if (name === exportName)
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
143
151
|
return !esm.length;
|
|
144
152
|
}
|
|
145
153
|
|
|
@@ -1,4 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
import {types} from 'putout';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
isIdentifier,
|
|
5
|
+
isVariableDeclaration,
|
|
6
|
+
isFunction,
|
|
7
|
+
isExportNamedDeclaration,
|
|
8
|
+
} = types;
|
|
9
|
+
|
|
10
|
+
export const report = (path) => {
|
|
11
|
+
const {type} = path;
|
|
12
|
+
|
|
13
|
+
if (isExportNamedDeclaration(path)) {
|
|
14
|
+
const {declaration} = path.node;
|
|
15
|
+
|
|
16
|
+
if (isFunction(declaration))
|
|
17
|
+
return `${type}:${declaration.id.name}`;
|
|
18
|
+
|
|
19
|
+
if (isVariableDeclaration(declaration)) {
|
|
20
|
+
const {declarations} = declaration;
|
|
21
|
+
|
|
22
|
+
if (declarations.length !== 1)
|
|
23
|
+
return type;
|
|
24
|
+
|
|
25
|
+
const [first] = declarations;
|
|
26
|
+
const {id} = first;
|
|
27
|
+
|
|
28
|
+
if (isIdentifier(id))
|
|
29
|
+
return `${type}:${id.name}`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return type;
|
|
34
|
+
};
|
|
2
35
|
|
|
3
36
|
export const fix = (path) => {
|
|
4
37
|
path.node.leadingComments = [
|
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,
|