@putout/plugin-esm 8.1.0 → 8.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
@@ -34,8 +34,7 @@ npm i putout @putout/plugin-esm -D
34
34
 
35
35
  ## File rules
36
36
 
37
- - ✅ [apply-namespace-import-to-file](#apply-namespace-import-file]);
38
- - ✅ [apply-named-import-to-file](#apply-named-import-to-file);
37
+ - ✅ [apply-import-by-type-to-file](#apply-import-by-type-to-file]);
39
38
  - ✅ [apply-privately-imported-file](#apply-privately-imported-file);
40
39
  - ✅ [apply-js-imported-file](#apply-js-imported-file);
41
40
  - ✅ [resolve-imported-file](#resolve-imported-file);
@@ -62,8 +61,7 @@ npm i putout @putout/plugin-esm -D
62
61
  "esm/apply-js-imported-file": "off",
63
62
  "esm/resolve-imported-file": "off",
64
63
  "esm/shorten-imported-file": "off",
65
- "esm/apply-namespace-import-to-file": "off",
66
- "esm/apply-named-import-to-file": "off",
64
+ "esm/apply-import-by-type-to-file": "off",
67
65
  "esm/apply-privately-imported-file": "off",
68
66
  "esm/remove-useless-export-specifiers": "off"
69
67
  }
@@ -401,7 +399,7 @@ import('foo.json', {
401
399
 
402
400
  ## File Rules
403
401
 
404
- ### apply-named-import-to-file
402
+ ### apply-import-by-type-to-file
405
403
 
406
404
  The rule fixes:
407
405
 
@@ -409,10 +407,14 @@ The rule fixes:
409
407
 
410
408
  Check out in 🐊**Putout Editor**:
411
409
 
410
+ - ✅ [`apply-namespace-import-to-file`](https://putout.cloudcmd.io/#/gist/1492d584559e5798325047de679222a0/c6a37a803b80823de1b64ab944f2427aecefb51b);
412
411
  - ✅ [`get-imports`](https://putout.cloudcmd.io/#/gist/5d7687215e9fbdf705935c444503dded/75a98d2db9d3847c73017e41637924b1cfd5a598);
413
412
  - ✅ [`has-export-default`](https://putout.cloudcmd.io/#/gist/b50ccfe5cc8c0c97e2fc98b37903ade4/fbc026e6f1027581f7aa4879dcafcaa7754bf8f4);
413
+ - ✅ [`apply-namespace-import`](https://putout.cloudcmd.io/#/gist/23a6dc6741b772c03fbed95feda2b451/1fbecac6fc40282bcda0593aa666a8c213ef85b7);
414
414
  - ✅ [`is-esm`](https://putout.cloudcmd.io/#/gist/fa080be2bf3a6560e289d84b5873c2bc/2601091f6bf97148843767968c3afcb36dde31de);
415
415
 
416
+ #### named import
417
+
416
418
  Let's consider file structure:
417
419
 
418
420
  ```
@@ -424,31 +426,19 @@ Let's consider file structure:
424
426
 
425
427
  In this case `index.js` can be fixed:
426
428
 
427
- #### ❌ Example of incorrect code
429
+ ##### ❌ Example of incorrect code
428
430
 
429
431
  ```js
430
432
  import a from './a.js';
431
433
  ```
432
434
 
433
- #### ✅ Example of correct code
435
+ ##### ✅ Example of correct code
434
436
 
435
437
  ```js
436
438
  import {a} from './a.js';
437
439
  ```
438
440
 
439
- ### apply-namespace-import-to-file
440
-
441
- The rule fixes:
442
-
443
- > `SyntaxError: The requested module './a.js' does not provide an export named 'default'`
444
-
445
- Check out in 🐊**Putout Editor**:
446
-
447
- - ✅ [`apply-namespace-import-to-file`](https://putout.cloudcmd.io/#/gist/1492d584559e5798325047de679222a0/c6a37a803b80823de1b64ab944f2427aecefb51b);
448
- - ✅ [`get-imports`](https://putout.cloudcmd.io/#/gist/5d7687215e9fbdf705935c444503dded/75a98d2db9d3847c73017e41637924b1cfd5a598);
449
- - ✅ [`has-export-default`](https://putout.cloudcmd.io/#/gist/b50ccfe5cc8c0c97e2fc98b37903ade4/fbc026e6f1027581f7aa4879dcafcaa7754bf8f4);
450
- - ✅ [`apply-namespace-import`](https://putout.cloudcmd.io/#/gist/23a6dc6741b772c03fbed95feda2b451/1fbecac6fc40282bcda0593aa666a8c213ef85b7);
451
- - ✅ [`is-esm`](https://putout.cloudcmd.io/#/gist/fa080be2bf3a6560e289d84b5873c2bc/2601091f6bf97148843767968c3afcb36dde31de);
441
+ #### namespace import
452
442
 
453
443
  Let's consider file structure:
454
444
 
@@ -461,13 +451,13 @@ Let's consider file structure:
461
451
 
462
452
  In this case `index.js` can be fixed:
463
453
 
464
- #### ❌ Example of incorrect code
454
+ ##### ❌ Example of incorrect code
465
455
 
466
456
  ```js
467
457
  import a from './a.js';
468
458
  ```
469
459
 
470
- #### ✅ Example of correct code
460
+ ##### ✅ Example of correct code
471
461
 
472
462
  ```js
473
463
  import * as a from './a.js';
@@ -0,0 +1,59 @@
1
+ import {putout, operator} from 'putout';
2
+ import * as hasExportDefaultPlugin from '#has-export-default';
3
+ import * as isESMPlugin from '#is-esm';
4
+
5
+ const isESM = (a) => a.rule === 'is-esm';
6
+ const hasExportDefault = (a) => a.rule === 'has-export-default';
7
+
8
+ const {
9
+ findFile,
10
+ readFileContent,
11
+ } = operator;
12
+
13
+ export const determineImportType = ({name, rootPath, importedFilename, privateImports}) => {
14
+ const parsedName = parseImportedFilename({
15
+ importedFilename,
16
+ privateImports,
17
+ });
18
+
19
+ const [importedFile] = findFile(rootPath, parsedName);
20
+
21
+ if (!importedFile)
22
+ return '';
23
+
24
+ const importedContent = readFileContent(importedFile);
25
+
26
+ const {places} = putout(importedContent, {
27
+ fix: false,
28
+ plugins: [
29
+ ['has-export-default', hasExportDefaultPlugin],
30
+ ['is-esm', isESMPlugin],
31
+ ],
32
+ });
33
+
34
+ const esm = places.filter(isESM);
35
+
36
+ if (!esm.length)
37
+ return '';
38
+
39
+ const defaultExport = places.filter(hasExportDefault);
40
+
41
+ if (defaultExport.length)
42
+ return '';
43
+
44
+ for (const {message} of esm) {
45
+ const [, exportName] = message.split(':');
46
+
47
+ if (name === exportName)
48
+ return 'equal';
49
+ }
50
+
51
+ return 'named';
52
+ };
53
+
54
+ function parseImportedFilename({importedFilename, privateImports}) {
55
+ if (privateImports.has(importedFilename))
56
+ return privateImports.get(importedFilename);
57
+
58
+ return importedFilename;
59
+ }
@@ -1,45 +1,48 @@
1
1
  import {join, dirname} from 'node:path';
2
2
  import {tryCatch} from 'try-catch';
3
- import putout, {
3
+ import {
4
4
  parse,
5
5
  print,
6
6
  transform,
7
7
  operator,
8
8
  } from 'putout';
9
9
  import {createGetPrivateImports} from '#private-imports';
10
- import * as isESMPlugin from '#is-esm';
11
- import * as hasExportDefaultPlugin from '#has-export-default';
12
10
  import * as getImportsPlugin from '#get-default-imports';
13
- import * as applyNamespaceImportPlugin from './apply-namespace-import/index.js';
11
+ import {determineImportType} from '#determine-import-type';
12
+ import {transformNamedImport} from './transform-named-import.js';
13
+ import {transformNamespaceImport} from './transform-namespace-import.js';
14
14
 
15
15
  const {
16
- findFile,
17
16
  getFilename,
18
17
  readFileContent,
19
18
  writeFileContent,
20
19
  } = operator;
21
20
 
22
21
  const getMessage = (a) => a.message;
23
- const isESM = (a) => a.rule === 'is-esm';
24
- const hasExportDefault = (a) => a.rule === 'has-export-default';
25
22
 
26
- export const report = (file, {name, source}) => {
23
+ export const report = (file, {importType, name, source}) => {
27
24
  const filename = getFilename(file);
28
- return `Use 'import * as ${name} from '${source}' in '${filename}'`;
25
+
26
+ if (importType === 'equal')
27
+ return `Use \`import {${name}} from '${source}'\` in '${filename}'`;
28
+
29
+ return `Use \`import * as ${name} from '${source}'\` in '${filename}'`;
29
30
  };
30
31
 
31
- export const fix = (file, {name, source, content, ast}) => {
32
- transform(ast, content, {
33
- rules: {
34
- 'apply-namespace-import': ['on', {
35
- name,
36
- source,
37
- }],
38
- },
39
- plugins: [
40
- ['apply-namespace-import', applyNamespaceImportPlugin],
41
- ],
42
- });
32
+ export const fix = (file, {name, source, content, ast, importType}) => {
33
+ if (importType === 'equal')
34
+ transformNamedImport(ast, {
35
+ name,
36
+ source,
37
+ content,
38
+ });
39
+
40
+ if (importType === 'named')
41
+ transformNamespaceImport(ast, {
42
+ name,
43
+ source,
44
+ content,
45
+ });
43
46
 
44
47
  const newContent = print(ast);
45
48
 
@@ -68,17 +71,18 @@ export const scan = (rootPath, {push, trackFile}) => {
68
71
  });
69
72
 
70
73
  for (const [name, source, importedFilename] of importsTuples) {
71
- const is = hasImportDefault({
74
+ const importType = determineImportType({
72
75
  name,
73
76
  rootPath,
74
77
  importedFilename,
75
78
  privateImports,
76
79
  });
77
80
 
78
- if (is)
81
+ if (!importType)
79
82
  continue;
80
83
 
81
84
  push(file, {
85
+ importType,
82
86
  name,
83
87
  source,
84
88
  ast,
@@ -107,50 +111,6 @@ function getImports(file, content, ast) {
107
111
  return buildImports(dir, imports);
108
112
  }
109
113
 
110
- function parseImportedFilename({importedFilename, privateImports}) {
111
- if (privateImports.has(importedFilename))
112
- return privateImports.get(importedFilename);
113
-
114
- return importedFilename;
115
- }
116
-
117
- function hasImportDefault({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 true;
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
- const defaultExport = places.filter(hasExportDefault);
140
-
141
- if (defaultExport.length)
142
- return true;
143
-
144
- for (const {message} of esm) {
145
- const [, exportName] = message.split(':');
146
-
147
- if (name === exportName)
148
- return true;
149
- }
150
-
151
- return !esm.length;
152
- }
153
-
154
114
  function parseFull(dir, source) {
155
115
  if (source.startsWith('#'))
156
116
  return source;
@@ -0,0 +1,16 @@
1
+ import {transform} from 'putout';
2
+ import * as applyNamedImportPlugin from './apply-named-import/index.js';
3
+
4
+ export const transformNamedImport = (ast, {name, source, content}) => {
5
+ transform(ast, content, {
6
+ rules: {
7
+ 'apply-named-import': ['on', {
8
+ name,
9
+ source,
10
+ }],
11
+ },
12
+ plugins: [
13
+ ['apply-named-import', applyNamedImportPlugin],
14
+ ],
15
+ });
16
+ };
@@ -0,0 +1,16 @@
1
+ import {transform} from 'putout';
2
+ import * as applyNamespaceImportPlugin from './apply-namespace-import/index.js';
3
+
4
+ export const transformNamespaceImport = (ast, {source, content, name}) => {
5
+ transform(ast, content, {
6
+ rules: {
7
+ 'apply-namespace-import': ['on', {
8
+ name,
9
+ source,
10
+ }],
11
+ },
12
+ plugins: [
13
+ ['apply-namespace-import', applyNamespaceImportPlugin],
14
+ ],
15
+ });
16
+ };
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as applyExportFrom from './apply-export-from/index.js';
2
2
  import * as applyJsImportedFile from './apply-js-imported-file/index.js';
3
+ import * as applyImportByTypeToFile from './apply-import-by-type-to-file/index.js';
3
4
  import * as mergeExportDeclarations from './merge-export-declarations/index.js';
4
5
  import * as removeUselessExportSpecifiers from './remove-useless-export-specifiers/index.js';
5
6
  import * as mergeDeclarationWithExport from './merge-declaration-with-export/index.js';
@@ -12,8 +13,6 @@ import * as removeEmptyImport from './remove-empty-import/index.js';
12
13
  import * as removeEmptyExport from './remove-empty-export/index.js';
13
14
  import * as mergeDuplicateImports from './merge-duplicate-imports/index.js';
14
15
  import * as convertAssertToWith from './convert-assert-to-with/index.js';
15
- import * as applyNamespaceImportToFile from './apply-namespace-import-to-file/index.js';
16
- import * as applyNamedImportToFile from './apply-named-import-to-file/index.js';
17
16
  import * as applyPrivatelyImportedFile from './apply-privately-imported-file/index.js';
18
17
  import * as resolveImportedFile from './resolve-imported-file/index.js';
19
18
  import * as shortenImportedFile from './shorten-imported-file/index.js';
@@ -35,8 +34,7 @@ export const rules = {
35
34
 
36
35
  'resolve-imported-file': ['off', resolveImportedFile],
37
36
  'shorten-imported-file': ['off', shortenImportedFile],
38
- 'apply-named-import-to-file': ['off', applyNamedImportToFile],
39
- 'apply-namespace-import-to-file': ['off', applyNamespaceImportToFile],
37
+ 'apply-import-by-type-to-file': ['off', applyImportByTypeToFile],
40
38
  'apply-privately-imported-file': ['off', applyPrivatelyImportedFile],
41
39
  'apply-js-imported-file': ['off', applyJsImportedFile],
42
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/plugin-esm",
3
- "version": "8.1.0",
3
+ "version": "8.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",
@@ -12,11 +12,12 @@
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
+ "#get-default-imports": "./lib/apply-import-by-type-to-file/get-imports/index.js",
16
16
  "#change-imports": "./lib/resolve-imported-file/change-imports/index.js",
17
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"
18
+ "#is-esm": "./lib/apply-import-by-type-to-file/is-esm/index.js",
19
+ "#has-export-default": "./lib/apply-import-by-type-to-file/has-export-default/index.js",
20
+ "#determine-import-type": "./lib/apply-import-by-type-to-file/determine-import-type.js"
20
21
  },
21
22
  "release": false,
22
23
  "tag": false,
@@ -1,176 +0,0 @@
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
- }