@parcel/transformer-less 2.0.0-nightly.97 → 2.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.
@@ -5,45 +5,89 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _plugin = require("@parcel/plugin");
8
+ function _path() {
9
+ const data = _interopRequireDefault(require("path"));
10
+
11
+ _path = function () {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _plugin() {
19
+ const data = require("@parcel/plugin");
20
+
21
+ _plugin = function () {
22
+ return data;
23
+ };
24
+
25
+ return data;
26
+ }
27
+
28
+ function _sourceMap() {
29
+ const data = _interopRequireDefault(require("@parcel/source-map"));
30
+
31
+ _sourceMap = function () {
32
+ return data;
33
+ };
34
+
35
+ return data;
36
+ }
37
+
38
+ function _less() {
39
+ const data = _interopRequireDefault(require("less"));
40
+
41
+ _less = function () {
42
+ return data;
43
+ };
44
+
45
+ return data;
46
+ }
47
+
48
+ var _loadConfig = require("./loadConfig");
49
+
50
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
51
 
10
52
  // E.g: ~library/file.less
11
53
  const WEBPACK_ALIAS_RE = /^~[^/]/;
12
54
 
13
- var _default = new _plugin.Transformer({
14
- async getConfig({
15
- asset,
16
- resolve
55
+ var _default = new (_plugin().Transformer)({
56
+ loadConfig({
57
+ config
17
58
  }) {
18
- let config = await asset.getConfig(['.lessrc', '.lessrc.js'], {
19
- packageKey: 'less'
59
+ return (0, _loadConfig.load)({
60
+ config
20
61
  });
21
-
22
- if (config === null) {
23
- config = {};
24
- }
25
-
26
- config.filename = asset.filePath;
27
- config.plugins = [...(config.plugins || []), urlPlugin({
28
- asset
29
- }), resolvePathPlugin({
30
- asset,
31
- resolve
32
- })];
33
- return config;
34
62
  },
35
63
 
36
64
  async transform({
37
65
  asset,
38
66
  options,
39
- config
67
+ config,
68
+ resolve
40
69
  }) {
41
- const less = await options.packageManager.require('less', asset.filePath);
42
- const code = await asset.getCode();
43
- let css;
70
+ asset.type = 'css';
71
+ asset.meta.hasDependencies = false;
72
+ let code = await asset.getCode();
73
+ let result;
44
74
 
45
75
  try {
46
- css = (await less.render(code, config)).css;
76
+ let lessConfig = config ? { ...config.config
77
+ } : {};
78
+
79
+ if (asset.env.sourceMap) {
80
+ lessConfig.sourceMap = {};
81
+ }
82
+
83
+ lessConfig.filename = asset.filePath;
84
+ lessConfig.plugins = [...(lessConfig.plugins || []), urlPlugin({
85
+ asset
86
+ }), resolvePathPlugin({
87
+ asset,
88
+ resolve
89
+ })];
90
+ result = await _less().default.render(code, lessConfig);
47
91
  } catch (err) {
48
92
  // For the error reporter
49
93
  err.fileName = err.filename;
@@ -54,9 +98,16 @@ var _default = new _plugin.Transformer({
54
98
  throw err;
55
99
  }
56
100
 
57
- asset.type = 'css';
58
- asset.setCode(css);
59
- asset.meta.hasDependencies = false;
101
+ if (result.map != null) {
102
+ let map = new (_sourceMap().default)(options.projectRoot);
103
+ let rawMap = JSON.parse(result.map);
104
+ map.addVLQMap({ ...rawMap,
105
+ sources: rawMap.sources.map(s => _path().default.relative(options.projectRoot, s))
106
+ });
107
+ asset.setMap(map);
108
+ }
109
+
110
+ asset.setCode(result.css);
60
111
  return [asset];
61
112
  }
62
113
 
@@ -69,13 +120,22 @@ function urlPlugin({
69
120
  }) {
70
121
  return {
71
122
  install(less, pluginManager) {
123
+ // This is a hack; no such interface exists, even conceptually, in Less.
72
124
  const visitor = new less.visitors.Visitor({
73
125
  visitUrl(node) {
74
- node.value.value = asset.addURLDependency(node.value.value, node.currentFileInfo.filename);
126
+ const valueNode = node.value;
127
+ const stringValue = valueNode.value;
128
+
129
+ if (!stringValue.startsWith('#') // IE's `behavior: url(#default#VML)`)
130
+ ) {
131
+ valueNode.value = asset.addURLDependency(stringValue, {});
132
+ }
133
+
75
134
  return node;
76
135
  }
77
136
 
78
- });
137
+ }); // $FlowFixMe[method-unbinding]
138
+
79
139
  visitor.run = visitor.visit;
80
140
  pluginManager.addVisitor(visitor);
81
141
  }
@@ -98,24 +158,65 @@ function resolvePathPlugin({
98
158
  return false;
99
159
  }
100
160
 
101
- async loadFile(rawFilename, ...args) {
161
+ async loadFile(rawFilename, currentDirectory, options) {
102
162
  let filename = rawFilename;
103
163
 
104
164
  if (WEBPACK_ALIAS_RE.test(filename)) {
105
165
  let correctPath = filename.replace(/^~/, '');
106
166
  throw new Error(`The @import path "${filename}" is using webpack specific syntax, which isn't supported by Parcel.\n\nTo @import files from node_modules, use "${correctPath}"`);
167
+ } // Based on https://github.com/less/less.js/blob/master/packages/less/src/less-node/file-manager.js
168
+
169
+
170
+ let isAbsoluteFilename = this.isPathAbsolute(filename);
171
+ let paths = isAbsoluteFilename ? [''] : [currentDirectory];
172
+
173
+ if (options.paths) {
174
+ paths.push(...options.paths);
107
175
  }
108
176
 
109
- try {
110
- return await super.loadFile(filename, ...args);
111
- } catch (err) {
112
- if (err.type !== 'File') {
113
- throw err;
177
+ let prefixes = options.prefixes || [''];
178
+ let fileParts = this.extractUrlParts(filename);
179
+ let filePath;
180
+ let contents;
181
+
182
+ if (filename[0] !== '~') {
183
+ outer: for (let p of paths) {
184
+ for (let prefix of prefixes) {
185
+ filePath = fileParts.rawPath + prefix + fileParts.filename;
186
+
187
+ if (p) {
188
+ filePath = _path().default.join(p, filePath);
189
+ }
190
+
191
+ if (options.ext) {
192
+ filePath = this.tryAppendExtension(filePath, options.ext);
193
+ }
194
+
195
+ try {
196
+ contents = await asset.fs.readFile(filePath, 'utf8');
197
+ break outer;
198
+ } catch (err) {
199
+ asset.invalidateOnFileCreate({
200
+ filePath
201
+ });
202
+ }
203
+ }
114
204
  }
205
+ }
115
206
 
116
- filename = await resolve(asset.filePath, filename);
117
- return super.loadFile(filename, ...args);
207
+ if (!contents) {
208
+ filePath = await resolve(asset.filePath, filename);
209
+ contents = await asset.fs.readFile(filePath, 'utf8');
118
210
  }
211
+
212
+ if (filePath) {
213
+ asset.invalidateOnFileChange(filePath);
214
+ }
215
+
216
+ return {
217
+ contents,
218
+ filename: filePath
219
+ };
119
220
  }
120
221
 
121
222
  }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.load = load;
7
+
8
+ function _path() {
9
+ const data = _interopRequireDefault(require("path"));
10
+
11
+ _path = function () {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
+
20
+ async function load({
21
+ config
22
+ }) {
23
+ let configFile = await config.getConfig(['.lessrc', '.lessrc.js'], {
24
+ packageKey: 'less'
25
+ });
26
+ let configContents = {};
27
+
28
+ if (configFile != null) {
29
+ configContents = configFile.contents; // Resolve relative paths from config file
30
+
31
+ if (configContents.paths) {
32
+ configContents.paths = configContents.paths.map(p => _path().default.resolve(_path().default.dirname(configFile.filePath), p));
33
+ }
34
+ } // Rewrites urls to be relative to the provided filename
35
+
36
+
37
+ configContents.rewriteUrls = 'all';
38
+ configContents.plugins = configContents.plugins || []; // This should enforce the config to be reloaded on every run as it's JS
39
+
40
+ let isDynamic = configFile && _path().default.extname(configFile.filePath) === '.js';
41
+
42
+ if (isDynamic) {
43
+ config.invalidateOnStartup();
44
+ }
45
+
46
+ return {
47
+ isStatic: !isDynamic,
48
+ config: configContents
49
+ };
50
+ }
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@parcel/transformer-less",
3
- "version": "2.0.0-nightly.97+a63f3fc9",
3
+ "version": "2.1.0",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
+ "funding": {
9
+ "type": "opencollective",
10
+ "url": "https://opencollective.com/parcel"
11
+ },
8
12
  "repository": {
9
13
  "type": "git",
10
14
  "url": "https://github.com/parcel-bundler/parcel.git"
@@ -12,14 +16,13 @@
12
16
  "main": "lib/LessTransformer.js",
13
17
  "source": "src/LessTransformer.js",
14
18
  "engines": {
15
- "node": ">= 10.0.0",
16
- "parcel": "^2.0.0-alpha.1.1"
19
+ "node": ">= 12.0.0",
20
+ "parcel": "^2.1.0"
17
21
  },
18
22
  "dependencies": {
19
- "@parcel/plugin": "2.0.0-nightly.97+a63f3fc9"
20
- },
21
- "devDependencies": {
22
- "less": "^3.9.0"
23
+ "@parcel/plugin": "^2.1.0",
24
+ "@parcel/source-map": "^2.0.0",
25
+ "less": "^4.1.1"
23
26
  },
24
- "gitHead": "a63f3fc9726483219412920faeb255e035f90747"
27
+ "gitHead": "5a80fbe4af8797d0aab248a66f5254f42f00e83e"
25
28
  }
@@ -1,36 +1,48 @@
1
- // @flow strict-local
2
-
1
+ // @flow
2
+ import {typeof default as Less} from 'less';
3
+ import path from 'path';
3
4
  import {Transformer} from '@parcel/plugin';
5
+ import SourceMap from '@parcel/source-map';
6
+ import less from 'less';
7
+
8
+ import {load} from './loadConfig';
4
9
 
5
10
  // E.g: ~library/file.less
6
11
  const WEBPACK_ALIAS_RE = /^~[^/]/;
7
12
 
8
- export default new Transformer({
9
- async getConfig({asset, resolve}) {
10
- let config = await asset.getConfig(['.lessrc', '.lessrc.js'], {
11
- packageKey: 'less',
12
- });
13
-
14
- if (config === null) {
15
- config = {};
16
- }
13
+ type LessConfig = {
14
+ sourceMap: any,
15
+ filename: string,
16
+ plugins: Array<any>,
17
+ ...
18
+ };
17
19
 
18
- config.filename = asset.filePath;
19
- config.plugins = [
20
- ...(config.plugins || []),
21
- urlPlugin({asset}),
22
- resolvePathPlugin({asset, resolve}),
23
- ];
24
-
25
- return config;
20
+ export default (new Transformer({
21
+ loadConfig({config}) {
22
+ return load({config});
26
23
  },
27
24
 
28
- async transform({asset, options, config}) {
29
- const less = await options.packageManager.require('less', asset.filePath);
30
- const code = await asset.getCode();
31
- let css;
25
+ async transform({asset, options, config, resolve}) {
26
+ asset.type = 'css';
27
+ asset.meta.hasDependencies = false;
28
+
29
+ let code = await asset.getCode();
30
+ let result;
32
31
  try {
33
- css = (await less.render(code, config)).css;
32
+ let lessConfig: LessConfig = config ? {...config.config} : {};
33
+
34
+ if (asset.env.sourceMap) {
35
+ lessConfig.sourceMap = {};
36
+ }
37
+
38
+ lessConfig.filename = asset.filePath;
39
+ lessConfig.plugins = [
40
+ ...(lessConfig.plugins || []),
41
+ urlPlugin({asset}),
42
+ resolvePathPlugin({asset, resolve}),
43
+ ];
44
+
45
+ result = await less.render(code, lessConfig);
34
46
  } catch (err) {
35
47
  // For the error reporter
36
48
  err.fileName = err.filename;
@@ -41,26 +53,42 @@ export default new Transformer({
41
53
  throw err;
42
54
  }
43
55
 
44
- asset.type = 'css';
45
- asset.setCode(css);
46
- asset.meta.hasDependencies = false;
56
+ if (result.map != null) {
57
+ let map = new SourceMap(options.projectRoot);
58
+ let rawMap = JSON.parse(result.map);
59
+ map.addVLQMap({
60
+ ...rawMap,
61
+ sources: rawMap.sources.map(s => path.relative(options.projectRoot, s)),
62
+ });
63
+ asset.setMap(map);
64
+ }
65
+
66
+ asset.setCode(result.css);
67
+
47
68
  return [asset];
48
69
  },
49
- });
70
+ }): Transformer);
50
71
 
51
72
  function urlPlugin({asset}) {
52
73
  return {
53
- install(less, pluginManager) {
74
+ install(less: Less, pluginManager) {
75
+ // This is a hack; no such interface exists, even conceptually, in Less.
76
+ type LessNodeWithValue = less.tree.Node & {value: any, ...};
77
+
54
78
  const visitor = new less.visitors.Visitor({
55
79
  visitUrl(node) {
56
- node.value.value = asset.addURLDependency(
57
- node.value.value,
58
- node.currentFileInfo.filename,
59
- );
80
+ const valueNode = ((node.value: any): LessNodeWithValue);
81
+ const stringValue = (valueNode.value: string);
82
+ if (
83
+ !stringValue.startsWith('#') // IE's `behavior: url(#default#VML)`)
84
+ ) {
85
+ valueNode.value = asset.addURLDependency(stringValue, {});
86
+ }
60
87
  return node;
61
88
  },
62
89
  });
63
90
 
91
+ // $FlowFixMe[method-unbinding]
64
92
  visitor.run = visitor.visit;
65
93
  pluginManager.addVisitor(visitor);
66
94
  },
@@ -79,7 +107,7 @@ function resolvePathPlugin({asset, resolve}) {
79
107
  return false;
80
108
  }
81
109
 
82
- async loadFile(rawFilename, ...args) {
110
+ async loadFile(rawFilename, currentDirectory, options) {
83
111
  let filename = rawFilename;
84
112
 
85
113
  if (WEBPACK_ALIAS_RE.test(filename)) {
@@ -89,15 +117,53 @@ function resolvePathPlugin({asset, resolve}) {
89
117
  );
90
118
  }
91
119
 
92
- try {
93
- return await super.loadFile(filename, ...args);
94
- } catch (err) {
95
- if (err.type !== 'File') {
96
- throw err;
120
+ // Based on https://github.com/less/less.js/blob/master/packages/less/src/less-node/file-manager.js
121
+ let isAbsoluteFilename = this.isPathAbsolute(filename);
122
+ let paths = isAbsoluteFilename ? [''] : [currentDirectory];
123
+ if (options.paths) {
124
+ paths.push(...options.paths);
125
+ }
126
+
127
+ let prefixes = options.prefixes || [''];
128
+ let fileParts = this.extractUrlParts(filename);
129
+ let filePath;
130
+ let contents;
131
+
132
+ if (filename[0] !== '~') {
133
+ outer: for (let p of paths) {
134
+ for (let prefix of prefixes) {
135
+ filePath = fileParts.rawPath + prefix + fileParts.filename;
136
+ if (p) {
137
+ filePath = path.join(p, filePath);
138
+ }
139
+
140
+ if (options.ext) {
141
+ filePath = this.tryAppendExtension(filePath, options.ext);
142
+ }
143
+
144
+ try {
145
+ contents = await asset.fs.readFile(filePath, 'utf8');
146
+ break outer;
147
+ } catch (err) {
148
+ asset.invalidateOnFileCreate({filePath});
149
+ }
150
+ }
97
151
  }
98
- filename = await resolve(asset.filePath, filename);
99
- return super.loadFile(filename, ...args);
100
152
  }
153
+
154
+ if (!contents) {
155
+ filePath = await resolve(asset.filePath, filename);
156
+ contents = await asset.fs.readFile(filePath, 'utf8');
157
+ }
158
+
159
+ if (filePath) {
160
+ asset.invalidateOnFileChange(filePath);
161
+ }
162
+
163
+ return {
164
+ contents,
165
+ filename: filePath,
166
+ };
101
167
  }
102
168
  }
103
169
 
@@ -0,0 +1,42 @@
1
+ // @flow
2
+ import type {Config} from '@parcel/types';
3
+ import path from 'path';
4
+
5
+ type ConfigResult = {|
6
+ isStatic: boolean,
7
+ config: any,
8
+ |};
9
+
10
+ export async function load({
11
+ config,
12
+ }: {|
13
+ config: Config,
14
+ |}): Promise<ConfigResult> {
15
+ let configFile = await config.getConfig(['.lessrc', '.lessrc.js'], {
16
+ packageKey: 'less',
17
+ });
18
+
19
+ let configContents = {};
20
+ if (configFile != null) {
21
+ configContents = configFile.contents;
22
+
23
+ // Resolve relative paths from config file
24
+ if (configContents.paths) {
25
+ configContents.paths = configContents.paths.map(p =>
26
+ path.resolve(path.dirname(configFile.filePath), p),
27
+ );
28
+ }
29
+ }
30
+
31
+ // Rewrites urls to be relative to the provided filename
32
+ configContents.rewriteUrls = 'all';
33
+ configContents.plugins = configContents.plugins || [];
34
+
35
+ // This should enforce the config to be reloaded on every run as it's JS
36
+ let isDynamic = configFile && path.extname(configFile.filePath) === '.js';
37
+ if (isDynamic) {
38
+ config.invalidateOnStartup();
39
+ }
40
+
41
+ return {isStatic: !isDynamic, config: configContents};
42
+ }