@putout/engine-loader 11.0.1 → 11.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
@@ -59,39 +59,25 @@ const plugins = loadPlugins({
59
59
  });
60
60
  ```
61
61
 
62
- #### Babel Plugins
62
+ ### loadPluginsAsync
63
63
 
64
- You can use `babel plugins` with help of `babel/` prefix.
65
-
66
- *Example*
67
- Let's use two plugins:
68
-
69
- - `babel-plugin-transform-inline-consecutive-adds`
70
- - `@babel/plugin-codemod-object-assign-to-object-spread`
71
-
72
- `@putout/engine-loader` will gues the prefix of `plugin` :).
64
+ Load **ESM** plugins:
73
65
 
74
66
  ```js
67
+ const {loadPluginsAsync} = require('@putout/engine-loader');
68
+
75
69
  const pluginNames = [
76
- 'babel/transform-inline-consecutive-adds',
77
- 'babel/codemod-object-assign-to-object-spread',
70
+ 'remove-unusede-variables',
78
71
  ];
79
72
 
80
- const plugins = loadPlugins({
81
- pluginNames,
82
- });
83
- ```
84
-
85
- When you need to use imported babel plugin use `babelPlugin`:
86
-
87
- ```js
88
- import {babelPlugin} from '@putout/engine-loader';
89
- import transformBlockScoping from '@babel/plugin-transform-block-scoping';
73
+ const rules = {
74
+ 'remove-unused-variables': 'on',
75
+ };
90
76
 
91
- const plugins = loadPlugins({
92
- pluginNames: [
93
- ['babel/transform-inline-consecutive-adds', babelPlugin(transformBlockScoping, 'Optional message')],
94
- ],
77
+ const plugins = await loadPluginsAsync({
78
+ cache: true, //default
79
+ pluginNames,
80
+ rules,
95
81
  });
96
82
  ```
97
83
 
package/lib/index.js CHANGED
@@ -10,40 +10,16 @@ const parseProcessorNames = require('./parse-processor-names');
10
10
  const parseRules = require('./parse-rules');
11
11
  const validateRules = require('./validate-rules');
12
12
  const validatePlugin = require('./validate-plugin');
13
- const {babelPlugin} = require('./wrap-plugin');
13
+ const {mergeRules} = require('./merge-rules');
14
+ const {loadPluginsAsync} = require('./load-plugins-async');
14
15
 
15
16
  const isString = (a) => typeof a === 'string';
16
17
 
17
- const defaultOptions = () => Object.create(null);
18
-
19
- const mergeRules = ([rule, plugin], rules) => {
20
- for (const currentRule of rules) {
21
- if (currentRule.rule !== rule)
22
- continue;
23
-
24
- const {msg, options} = currentRule;
25
-
26
- return {
27
- rule,
28
- plugin,
29
- msg,
30
- options,
31
- };
32
- }
33
-
34
- return {
35
- rule,
36
- plugin,
37
- msg: '',
38
- options: defaultOptions(),
39
- };
40
- };
41
-
18
+ module.exports.loadPluginsAsync = loadPluginsAsync;
42
19
  module.exports.loadProcessorsAsync = nanomemoize(async (options, load) => {
43
20
  check(options);
44
21
 
45
22
  const {processors = []} = options;
46
-
47
23
  const parsedProcessors = parseProcessorNames(processors);
48
24
  const loadProcessor = createAsyncLoader('processor');
49
25
 
@@ -63,8 +39,6 @@ module.exports.loadProcessorsAsync = nanomemoize(async (options, load) => {
63
39
 
64
40
  module.exports.createAsyncLoader = createAsyncLoader;
65
41
 
66
- module.exports.babelPlugin = babelPlugin;
67
-
68
42
  module.exports.loadPlugins = (options) => {
69
43
  check(options);
70
44
 
@@ -121,12 +95,7 @@ function getLoadedRules(rules) {
121
95
  }
122
96
 
123
97
  function splitRule(rule) {
124
- const name = rule.replace('babel/', '');
125
-
126
- if (rule.startsWith('babel'))
127
- return [name, 'babel'];
128
-
129
- return [name, 'putout'];
98
+ return [rule, 'putout'];
130
99
  }
131
100
 
132
101
  function loadPlugins({items, loadedRules}) {
@@ -139,7 +108,6 @@ function loadPlugins({items, loadedRules}) {
139
108
  checkRule(rule);
140
109
 
141
110
  const [name, namespace] = splitRule(rule);
142
-
143
111
  const plugin = itemPlugin || loadPlugin({
144
112
  name,
145
113
  namespace,
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ const isEnabled = require('./is-enabled');
4
+ const parseRules = require('./parse-rules');
5
+ const {nanomemoize} = require('nano-memoize');
6
+ const {createAsyncLoader} = require('./async-loader');
7
+ const parsePluginNames = require('./parse-plugin-names');
8
+ const validateRules = require('./validate-rules');
9
+ const validatePlugin = require('./validate-plugin');
10
+ const {mergeRules} = require('./merge-rules');
11
+
12
+ const isString = (a) => typeof a === 'string';
13
+
14
+ module.exports.loadPluginsAsync = nanomemoize(async (options) => {
15
+ check(options);
16
+
17
+ const {pluginNames = [], rules = {}} = options;
18
+
19
+ const cookedRules = parseRules(rules);
20
+ const loadedRules = getLoadedRules(cookedRules);
21
+
22
+ const items = parsePluginNames(pluginNames);
23
+
24
+ const plugins = await loadPlugins({
25
+ items,
26
+ loadedRules,
27
+ });
28
+
29
+ validateRules({
30
+ rules,
31
+ items,
32
+ });
33
+
34
+ const result = [];
35
+
36
+ // Would be great to have ability to filter
37
+ // disabled plugins and prevent them from loading
38
+ // but we can't because of a way multi-rule plugins
39
+ // works. We can't determine count and names of all
40
+ // rules of a plugin before load.
41
+ for (const [name, plugin] of plugins) {
42
+ if (!isEnabled(name, cookedRules))
43
+ continue;
44
+
45
+ result.push(mergeRules(
46
+ [name, plugin],
47
+ cookedRules,
48
+ ));
49
+ }
50
+
51
+ return result;
52
+ });
53
+
54
+ function splitRule(rule) {
55
+ return [rule, 'putout'];
56
+ }
57
+
58
+ async function loadPlugins({items, loadedRules}) {
59
+ const loadPlugin = createAsyncLoader('plugin');
60
+ const promises = [];
61
+
62
+ for (const [rule, itemPlugin] of items) {
63
+ if (!isEnabled(rule, loadedRules))
64
+ continue;
65
+
66
+ checkRule(rule);
67
+
68
+ const [name] = splitRule(rule);
69
+ const plugin = itemPlugin || loadPlugin(name);
70
+
71
+ promises.push(plugin);
72
+ }
73
+
74
+ const resolvedPlugins = await Promise.all(promises);
75
+ const plugins = [];
76
+
77
+ for (const [i, [rule]] of items.entries()) {
78
+ const plugin = resolvedPlugins[i];
79
+
80
+ validatePlugin({
81
+ plugin,
82
+ rule,
83
+ });
84
+
85
+ const {rules} = plugin;
86
+
87
+ if (rules) {
88
+ plugins.push(...extendRules(rule, rules));
89
+ continue;
90
+ }
91
+
92
+ plugins.push([rule, plugin]);
93
+ }
94
+
95
+ return plugins;
96
+ }
97
+
98
+ function getLoadedRules(rules) {
99
+ const loadedRules = [];
100
+
101
+ for (const item of rules) {
102
+ const {rule} = item;
103
+
104
+ if (rule.includes('/'))
105
+ continue;
106
+
107
+ loadedRules.push(item);
108
+ }
109
+
110
+ return loadedRules;
111
+ }
112
+
113
+ function extendRules(rule, plugin) {
114
+ const result = [];
115
+ const entries = Object.entries(plugin);
116
+
117
+ for (const [name, plugin] of entries) {
118
+ result.push([`${rule}/${name}`, plugin]);
119
+ }
120
+
121
+ return result;
122
+ }
123
+
124
+ function check(options) {
125
+ if (!options || typeof options !== 'object')
126
+ throw Error('options should be an object!');
127
+ }
128
+
129
+ function checkRule(rule) {
130
+ if (!isString(rule))
131
+ throw Error(`☝️ Looks like plugin name type is not 'string', but: '${typeof rule}'`);
132
+ }
package/lib/load.js CHANGED
@@ -5,14 +5,9 @@ const {createRequire} = require('module');
5
5
  const tryCatch = require('try-catch');
6
6
  const once = require('once');
7
7
 
8
- const wrapPlugin = require('./wrap-plugin');
9
-
10
8
  const bigFirst = (a) => `${a[0].toUpperCase()}${a.slice(1)}`;
11
9
 
12
10
  const load = (type) => ({name, namespace}) => {
13
- if (namespace !== 'putout')
14
- return wrapPlugin(name, namespace);
15
-
16
11
  const [pluginPath, customRequire] = getPath(namespace, type, name);
17
12
 
18
13
  if (!pluginPath)
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const defaultOptions = () => Object.create(null);
4
+
5
+ module.exports.mergeRules = ([rule, plugin], rules) => {
6
+ for (const currentRule of rules) {
7
+ if (currentRule.rule !== rule)
8
+ continue;
9
+
10
+ const {msg, options} = currentRule;
11
+
12
+ return {
13
+ rule,
14
+ plugin,
15
+ msg,
16
+ options,
17
+ };
18
+ }
19
+
20
+ return {
21
+ rule,
22
+ plugin,
23
+ msg: '',
24
+ options: defaultOptions(),
25
+ };
26
+ };
@@ -78,34 +78,14 @@ function parseArray(rule, args) {
78
78
  };
79
79
  }
80
80
 
81
- const [, msg = '', options = defaultOptions()] = args;
82
-
83
- if (args.length === 2 && !isStr(msg)) {
84
- return {
85
- rule,
86
- state,
87
- plugin,
88
- msg: '',
89
- options: msg,
90
- };
91
- }
92
-
93
- if (args.length === 1 && isObj(rawState)) {
94
- return {
95
- rule,
96
- state,
97
- plugin,
98
- msg,
99
- options: rawState,
100
- };
101
- }
81
+ const [, msg = ''] = args;
102
82
 
103
83
  return {
104
84
  rule,
105
85
  state,
106
86
  plugin,
107
- msg,
108
- options,
87
+ msg: '',
88
+ options: msg,
109
89
  };
110
90
  }
111
91
 
@@ -1,9 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const parse = (rule) => {
4
- if (rule.startsWith('babel/'))
5
- return rule;
6
-
7
4
  if (rule.includes('/'))
8
5
  return rule
9
6
  .split('/')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/engine-loader",
3
- "version": "11.0.1",
3
+ "version": "11.1.0",
4
4
  "type": "commonjs",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "load plugins and prepare them to run",
@@ -25,7 +25,6 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@putout/engine-parser": "^8.0.0",
28
- "@babel/core": "^7.12.3",
29
28
  "diff-match-patch": "^1.0.4",
30
29
  "nano-memoize": "^3.0.11",
31
30
  "once": "^1.4.0",
@@ -41,14 +40,11 @@
41
40
  "putout": "*"
42
41
  },
43
42
  "devDependencies": {
44
- "@babel/plugin-codemod-object-assign-to-object-spread": "^7.7.4",
45
43
  "@putout/formatter-progress": "*",
46
44
  "@putout/plugin-convert-commonjs-to-esm": "*",
47
45
  "@putout/plugin-remove-unused-variables": "*",
48
46
  "@putout/processor-javascript": "*",
49
47
  "@putout/processor-markdown": "*",
50
- "babel-plugin-angularjs-annotate": "https://github.com/putoutjs/babel-plugin-angularjs-annotate",
51
- "babel-plugin-transform-inline-consecutive-adds": "^0.4.3",
52
48
  "c8": "^8.0.0",
53
49
  "eslint": "^8.0.1",
54
50
  "eslint-plugin-n": "^16.0.0",
@@ -1,68 +0,0 @@
1
- 'use strict';
2
-
3
- const DiffMatchPatch = require('diff-match-patch');
4
-
5
- module.exports = (oldText, newText) => {
6
- const lines = diffLineMode(oldText, newText);
7
- const changedLines = getChangedLines(lines);
8
-
9
- return convert(changedLines);
10
- };
11
-
12
- function convert(lines) {
13
- const result = [];
14
- const column = 0;
15
-
16
- for (const line of lines) {
17
- result.push({
18
- line,
19
- column,
20
- });
21
- }
22
-
23
- return result;
24
- }
25
-
26
- const NOT_CHANGED = 0;
27
- const REMOVED = -1;
28
-
29
- function getChangedLines(lines) {
30
- let i = 0;
31
- const changedLines = [];
32
- let prevState = 0;
33
-
34
- for (const [state, line] of lines) {
35
- if (state === NOT_CHANGED) {
36
- ++i;
37
- continue;
38
- }
39
-
40
- if (state === REMOVED) {
41
- i += line.split('\n').length - 1;
42
- }
43
-
44
- if (prevState !== NOT_CHANGED) {
45
- prevState = state;
46
- continue;
47
- }
48
-
49
- changedLines.push(i);
50
- prevState = state;
51
- }
52
-
53
- return changedLines;
54
- }
55
-
56
- // https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
57
- function diffLineMode(text1, text2) {
58
- const dmp = new DiffMatchPatch();
59
- const a = dmp.diff_linesToChars_(text1, text2);
60
- const lineText1 = a.chars1;
61
- const lineText2 = a.chars2;
62
- const {lineArray} = a;
63
- const diffs = dmp.diff_main(lineText1, lineText2, false);
64
-
65
- dmp.diff_charsToLines_(diffs, lineArray);
66
-
67
- return diffs;
68
- }
@@ -1,22 +0,0 @@
1
- 'use strict';
2
-
3
- const {
4
- createConfigItem,
5
- transformFromAstSync,
6
- } = require('@babel/core');
7
-
8
- const isString = (a) => typeof a === 'string';
9
-
10
- module.exports = (ast, code, name) => {
11
- const plugin = !isString(name) ? name : require(name);
12
-
13
- transformFromAstSync(ast, code, {
14
- cloneInputAst: false,
15
- plugins: [
16
- // globally installed modules support
17
- createConfigItem(plugin),
18
- ],
19
- });
20
-
21
- return ast;
22
- };
@@ -1,82 +0,0 @@
1
- 'use strict';
2
-
3
- const tryCatch = require('try-catch');
4
- const {print} = require('@putout/engine-parser');
5
-
6
- const getPositions = require('./get-positions-by-diff');
7
-
8
- const babelTransform = require('./transforms/babel');
9
-
10
- const BABEL_REG = /@babel\/plugin-|babel-plugin-/;
11
-
12
- const getMessage = (a) => a.replace(BABEL_REG, '').replaceAll('-', ' ');
13
-
14
- const getModulePath = (name) => {
15
- const [, path] = tryCatch(require.resolve, name);
16
- return path;
17
- };
18
-
19
- module.exports = (name, namespace) => {
20
- const message = getMessage(name);
21
-
22
- if (/babel/.test(namespace))
23
- return getPlugin({
24
- name: getBabelPluginName(name),
25
- message,
26
- transform: babelTransform,
27
- });
28
-
29
- return null;
30
- };
31
-
32
- const getPlugin = ({name, transform, message}) => ({
33
- report: () => message,
34
- fix: () => {},
35
- find(ast, {push}) {
36
- const oldCode = print(ast, {
37
- printer: 'putout',
38
- });
39
-
40
- transform(ast, oldCode, name);
41
-
42
- const newCode = print(ast, {
43
- printer: 'putout',
44
- });
45
-
46
- if (newCode === oldCode)
47
- return;
48
-
49
- const positions = getPositions(oldCode, newCode);
50
-
51
- for (const start of positions) {
52
- const node = {
53
- loc: {
54
- start,
55
- },
56
- };
57
-
58
- const path = {
59
- node,
60
- };
61
-
62
- push(path);
63
- }
64
- },
65
- });
66
-
67
- module.exports.babelPlugin = (plugin, message) => {
68
- return getPlugin({
69
- name: plugin,
70
- transform: babelTransform,
71
- message,
72
- });
73
- };
74
-
75
- function getBabelPluginName(name) {
76
- const namespaced = getModulePath(`@babel/plugin-${name}`);
77
-
78
- if (namespaced)
79
- return namespaced;
80
-
81
- return `babel-plugin-${name}`;
82
- }