@parcel/transformer-sass 2.0.0-beta.1 → 2.0.0-nightly.1002

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,63 +5,102 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _plugin = require("@parcel/plugin");
8
+ function _plugin() {
9
+ const data = require("@parcel/plugin");
9
10
 
10
- var _utils = require("@parcel/utils");
11
+ _plugin = function () {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _path() {
19
+ const data = _interopRequireDefault(require("path"));
20
+
21
+ _path = function () {
22
+ return data;
23
+ };
24
+
25
+ return data;
26
+ }
27
+
28
+ function _os() {
29
+ const data = require("os");
30
+
31
+ _os = function () {
32
+ return data;
33
+ };
34
+
35
+ return data;
36
+ }
37
+
38
+ function _sourceMap() {
39
+ const data = _interopRequireDefault(require("@parcel/source-map"));
40
+
41
+ _sourceMap = function () {
42
+ return data;
43
+ };
11
44
 
12
- var _path = _interopRequireDefault(require("path"));
45
+ return data;
46
+ }
13
47
 
14
- var _os = require("os");
48
+ function _sass() {
49
+ const data = _interopRequireDefault(require("sass"));
15
50
 
16
- var _sourceMap = _interopRequireDefault(require("@parcel/source-map"));
51
+ _sass = function () {
52
+ return data;
53
+ };
54
+
55
+ return data;
56
+ }
57
+
58
+ function _util() {
59
+ const data = require("util");
60
+
61
+ _util = function () {
62
+ return data;
63
+ };
64
+
65
+ return data;
66
+ }
17
67
 
18
68
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
69
 
20
70
  // E.g: ~library/file.sass
21
- const WEBPACK_ALIAS_RE = /^~[^/]/;
71
+ const NODE_MODULE_ALIAS_RE = /^~[^/\\]/;
22
72
 
23
- var _default = new _plugin.Transformer({
73
+ var _default = new (_plugin().Transformer)({
24
74
  async loadConfig({
25
75
  config,
26
76
  options
27
77
  }) {
28
- let configFile = await config.getConfig(['.sassrc', '.sassrc.js'], {
78
+ let configFile = await config.getConfig(['.sassrc', '.sassrc.json', '.sassrc.js'], {
29
79
  packageKey: 'sass'
30
80
  });
31
- let configResult = {
32
- contents: configFile ? configFile.contents : {},
33
- isSerialisable: true
34
- };
35
-
36
- if (configFile && _path.default.extname(configFile.filePath) === '.js') {
37
- config.shouldInvalidateOnStartup();
38
- config.shouldReload();
39
- configResult.isSerialisable = false;
81
+ let configResult = configFile ? configFile.contents : {}; // Resolve relative paths from config file
82
+
83
+ if (configFile && configResult.includePaths) {
84
+ configResult.includePaths = configResult.includePaths.map(p => _path().default.resolve(_path().default.dirname(configFile.filePath), p));
40
85
  }
41
86
 
42
- if (configResult.contents.importer === undefined) {
43
- configResult.contents.importer = [];
44
- } else if (!Array.isArray(configResult.contents.importer)) {
45
- configResult.contents.importer = [configResult.contents.importer];
87
+ if (configFile && _path().default.extname(configFile.filePath) === '.js') {
88
+ config.invalidateOnStartup();
89
+ }
90
+
91
+ if (configResult.importer === undefined) {
92
+ configResult.importer = [];
93
+ } else if (!Array.isArray(configResult.importer)) {
94
+ configResult.importer = [configResult.importer];
46
95
  } // Always emit sourcemap
47
96
 
48
97
 
49
- configResult.contents.sourceMap = true; // sources are created relative to the directory of outFile
98
+ configResult.sourceMap = true; // sources are created relative to the directory of outFile
50
99
 
51
- configResult.contents.outFile = _path.default.join(options.projectRoot, 'style.css.map');
52
- configResult.contents.omitSourceMapUrl = true;
53
- configResult.contents.sourceMapContents = false;
54
- config.setResult(configResult);
55
- },
56
-
57
- preSerializeConfig({
58
- config
59
- }) {
60
- if (!config.result) return; // Ensure we dont try to serialise functions
61
-
62
- if (!config.result.isSerialisable) {
63
- config.result.contents = {};
64
- }
100
+ configResult.outFile = _path().default.join(options.projectRoot, 'style.css.map');
101
+ configResult.omitSourceMapUrl = true;
102
+ configResult.sourceMapContents = false;
103
+ return configResult;
65
104
  },
66
105
 
67
106
  async transform({
@@ -70,20 +109,20 @@ var _default = new _plugin.Transformer({
70
109
  config,
71
110
  resolve
72
111
  }) {
73
- let rawConfig = config ? config.contents : {};
74
- let sass = await options.packageManager.require('sass', asset.filePath, {
75
- autoinstall: options.autoinstall
76
- });
77
- const sassRender = (0, _utils.promisify)(sass.render.bind(sass));
112
+ let rawConfig = config !== null && config !== void 0 ? config : {};
113
+ let sassRender = (0, _util().promisify)(_sass().default.render.bind(_sass().default));
78
114
  let css;
79
115
 
80
116
  try {
81
117
  let code = await asset.getCode();
82
118
  let result = await sassRender({ ...rawConfig,
83
119
  file: asset.filePath,
84
- data: rawConfig.data ? rawConfig.data + _os.EOL + code : code,
120
+ data: rawConfig.data ? rawConfig.data + _os().EOL + code : code,
85
121
  importer: [...rawConfig.importer, resolvePathImporter({
86
- resolve
122
+ asset,
123
+ resolve,
124
+ includePaths: rawConfig.includePaths,
125
+ options
87
126
  })],
88
127
  indentedSyntax: typeof rawConfig.indentedSyntax === 'boolean' ? rawConfig.indentedSyntax : asset.type === 'sass'
89
128
  });
@@ -91,15 +130,13 @@ var _default = new _plugin.Transformer({
91
130
 
92
131
  for (let included of result.stats.includedFiles) {
93
132
  if (included !== asset.filePath) {
94
- asset.addIncludedFile({
95
- filePath: included
96
- });
133
+ asset.invalidateOnFileChange(included);
97
134
  }
98
135
  }
99
136
 
100
137
  if (result.map != null) {
101
- let map = new _sourceMap.default();
102
- map.addRawMappings(JSON.parse(result.map));
138
+ let map = new (_sourceMap().default)(options.projectRoot);
139
+ map.addVLQMap(JSON.parse(result.map));
103
140
  asset.setMap(map);
104
141
  }
105
142
  } catch (err) {
@@ -122,34 +159,97 @@ var _default = new _plugin.Transformer({
122
159
  exports.default = _default;
123
160
 
124
161
  function resolvePathImporter({
125
- resolve
162
+ asset,
163
+ resolve,
164
+ includePaths,
165
+ options
126
166
  }) {
127
- return function (rawUrl, prev, done) {
128
- let url = rawUrl.replace(/^file:\/\//, '');
167
+ // This is a reimplementation of the Sass resolution algorithm that uses Parcel's
168
+ // FS and tracks all tried files so they are watched for creation.
169
+ async function resolvePath(url, prev) {
170
+ /*
171
+ Imports are resolved by trying, in order:
172
+ * Loading a file relative to the file in which the `@import` appeared.
173
+ * Each custom importer.
174
+ * Loading a file relative to the current working directory (This rule doesn't really make sense for Parcel).
175
+ * Each load path in `includePaths`
176
+ * Each load path specified in the `SASS_PATH` environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
177
+ See: https://sass-lang.com/documentation/js-api#importer
178
+ See also: https://github.com/sass/dart-sass/blob/006e6aa62f2417b5267ad5cdb5ba050226fab511/lib/src/importer/node/implementation.dart
179
+ */
180
+ let paths = [_path().default.dirname(prev)];
181
+
182
+ if (includePaths) {
183
+ paths.push(...includePaths);
184
+ }
129
185
 
130
- if (WEBPACK_ALIAS_RE.test(url)) {
131
- const correctPath = url.replace(/^~/, '');
132
- const error = new Error(`The @import path "${url}" is using webpack specific syntax, which isn't supported by Parcel.\n\nTo @import files from node_modules, use "${correctPath}"`);
133
- done(error);
134
- return;
186
+ asset.invalidateOnEnvChange('SASS_PATH');
187
+
188
+ if (options.env.SASS_PATH) {
189
+ paths.push(...options.env.SASS_PATH.split(process.platform === 'win32' ? ';' : ':').map(p => _path().default.resolve(options.projectRoot, p)));
135
190
  }
136
191
 
137
- resolve(prev, url).then(resolvedPath => {
138
- done({
139
- file: resolvedPath
140
- });
141
- }).catch(() => {
142
- /*
143
- We return `null` instead of an error so that Sass' resolution algorithm can continue.
144
- Imports are resolved by trying, in order:
145
- * Loading a file relative to the file in which the `@import` appeared.
146
- * Each custom importer.
147
- * Loading a file relative to the current working directory.
148
- * Each load path in `includePaths`
149
- * Each load path specified in the `SASS_PATH` environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
150
- See: https://sass-lang.com/documentation/js-api#importer
151
- */
152
- done(null);
153
- });
192
+ const urls = [url];
193
+
194
+ const urlFileName = _path().default.basename(url);
195
+
196
+ if (urlFileName[0] !== '_') {
197
+ urls.push(_path().default.join(_path().default.dirname(url), `_${urlFileName}`));
198
+ }
199
+
200
+ if (url[0] !== '~') {
201
+ for (let p of paths) {
202
+ for (let u of urls) {
203
+ const filePath = _path().default.resolve(p, u);
204
+
205
+ try {
206
+ const contents = await asset.fs.readFile(filePath, 'utf8');
207
+ return {
208
+ filePath,
209
+ contents
210
+ };
211
+ } catch (err) {
212
+ asset.invalidateOnFileCreate({
213
+ filePath
214
+ });
215
+ }
216
+ }
217
+ }
218
+ } // If none of the default sass rules apply, try Parcel's resolver.
219
+
220
+
221
+ for (let u of urls) {
222
+ if (NODE_MODULE_ALIAS_RE.test(u)) {
223
+ u = u.slice(1);
224
+ }
225
+
226
+ try {
227
+ const filePath = await resolve(prev, u);
228
+
229
+ if (filePath) {
230
+ const contents = await asset.fs.readFile(filePath, 'utf8');
231
+ return {
232
+ filePath,
233
+ contents
234
+ };
235
+ }
236
+ } catch (err) {
237
+ continue;
238
+ }
239
+ }
240
+ }
241
+
242
+ return function (rawUrl, prev, done) {
243
+ const url = rawUrl.replace(/^file:\/\//, '');
244
+ resolvePath(url, prev).then(resolved => {
245
+ if (resolved) {
246
+ done({
247
+ file: resolved.filePath,
248
+ contents: resolved.contents
249
+ });
250
+ } else {
251
+ done();
252
+ }
253
+ }).catch(done);
154
254
  };
155
255
  }
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@parcel/transformer-sass",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-nightly.1002+5530a6ef",
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,20 +16,13 @@
12
16
  "main": "lib/SassTransformer.js",
13
17
  "source": "src/SassTransformer.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.0.0-nightly.1000+5530a6ef"
17
21
  },
18
22
  "dependencies": {
19
- "@parcel/fs": "2.0.0-beta.1",
20
- "@parcel/plugin": "2.0.0-beta.1",
21
- "@parcel/source-map": "2.0.0-alpha.4.13",
22
- "@parcel/utils": "2.0.0-beta.1"
23
- },
24
- "devDependencies": {
25
- "sass": "^1.22.9"
26
- },
27
- "peerDependencies": {
28
- "@parcel/core": "^2.0.0-alpha.3.1"
23
+ "@parcel/plugin": "2.0.0-nightly.1002+5530a6ef",
24
+ "@parcel/source-map": "^2.0.0",
25
+ "sass": "^1.38.0"
29
26
  },
30
- "gitHead": "74335525be92e23bac4ed1bf30595443cfb238e3"
27
+ "gitHead": "5530a6eff8b619873353baeb0457ae4ec591e9fa"
31
28
  }
@@ -1,66 +1,55 @@
1
1
  // @flow
2
2
  import {Transformer} from '@parcel/plugin';
3
- import {promisify} from '@parcel/utils';
4
3
  import path from 'path';
5
4
  import {EOL} from 'os';
6
5
  import SourceMap from '@parcel/source-map';
6
+ import sass from 'sass';
7
+ import {promisify} from 'util';
7
8
 
8
9
  // E.g: ~library/file.sass
9
- const WEBPACK_ALIAS_RE = /^~[^/]/;
10
+ const NODE_MODULE_ALIAS_RE = /^~[^/\\]/;
10
11
 
11
- export default new Transformer({
12
+ export default (new Transformer({
12
13
  async loadConfig({config, options}) {
13
- let configFile = await config.getConfig(['.sassrc', '.sassrc.js'], {
14
- packageKey: 'sass',
15
- });
14
+ let configFile = await config.getConfig(
15
+ ['.sassrc', '.sassrc.json', '.sassrc.js'],
16
+ {
17
+ packageKey: 'sass',
18
+ },
19
+ );
16
20
 
17
- let configResult = {
18
- contents: configFile ? configFile.contents : {},
19
- isSerialisable: true,
20
- };
21
+ let configResult = configFile ? configFile.contents : {};
21
22
 
22
- if (configFile && path.extname(configFile.filePath) === '.js') {
23
- config.shouldInvalidateOnStartup();
24
- config.shouldReload();
23
+ // Resolve relative paths from config file
24
+ if (configFile && configResult.includePaths) {
25
+ configResult.includePaths = configResult.includePaths.map(p =>
26
+ path.resolve(path.dirname(configFile.filePath), p),
27
+ );
28
+ }
25
29
 
26
- configResult.isSerialisable = false;
30
+ if (configFile && path.extname(configFile.filePath) === '.js') {
31
+ config.invalidateOnStartup();
27
32
  }
28
33
 
29
- if (configResult.contents.importer === undefined) {
30
- configResult.contents.importer = [];
31
- } else if (!Array.isArray(configResult.contents.importer)) {
32
- configResult.contents.importer = [configResult.contents.importer];
34
+ if (configResult.importer === undefined) {
35
+ configResult.importer = [];
36
+ } else if (!Array.isArray(configResult.importer)) {
37
+ configResult.importer = [configResult.importer];
33
38
  }
34
39
 
35
40
  // Always emit sourcemap
36
- configResult.contents.sourceMap = true;
41
+ configResult.sourceMap = true;
37
42
  // sources are created relative to the directory of outFile
38
- configResult.contents.outFile = path.join(
39
- options.projectRoot,
40
- 'style.css.map',
41
- );
42
- configResult.contents.omitSourceMapUrl = true;
43
- configResult.contents.sourceMapContents = false;
43
+ configResult.outFile = path.join(options.projectRoot, 'style.css.map');
44
+ configResult.omitSourceMapUrl = true;
45
+ configResult.sourceMapContents = false;
44
46
 
45
- config.setResult(configResult);
46
- },
47
-
48
- preSerializeConfig({config}) {
49
- if (!config.result) return;
50
-
51
- // Ensure we dont try to serialise functions
52
- if (!config.result.isSerialisable) {
53
- config.result.contents = {};
54
- }
47
+ return configResult;
55
48
  },
56
49
 
57
50
  async transform({asset, options, config, resolve}) {
58
- let rawConfig = config ? config.contents : {};
59
- let sass = await options.packageManager.require('sass', asset.filePath, {
60
- autoinstall: options.autoinstall,
61
- });
62
-
63
- const sassRender = promisify(sass.render.bind(sass));
51
+ let rawConfig = config ?? {};
52
+ let sassRender = promisify(sass.render.bind(sass));
64
53
  let css;
65
54
  try {
66
55
  let code = await asset.getCode();
@@ -68,7 +57,15 @@ export default new Transformer({
68
57
  ...rawConfig,
69
58
  file: asset.filePath,
70
59
  data: rawConfig.data ? rawConfig.data + EOL + code : code,
71
- importer: [...rawConfig.importer, resolvePathImporter({resolve})],
60
+ importer: [
61
+ ...rawConfig.importer,
62
+ resolvePathImporter({
63
+ asset,
64
+ resolve,
65
+ includePaths: rawConfig.includePaths,
66
+ options,
67
+ }),
68
+ ],
72
69
  indentedSyntax:
73
70
  typeof rawConfig.indentedSyntax === 'boolean'
74
71
  ? rawConfig.indentedSyntax
@@ -78,13 +75,13 @@ export default new Transformer({
78
75
  css = result.css;
79
76
  for (let included of result.stats.includedFiles) {
80
77
  if (included !== asset.filePath) {
81
- asset.addIncludedFile({filePath: included});
78
+ asset.invalidateOnFileChange(included);
82
79
  }
83
80
  }
84
81
 
85
82
  if (result.map != null) {
86
- let map = new SourceMap();
87
- map.addRawMappings(JSON.parse(result.map));
83
+ let map = new SourceMap(options.projectRoot);
84
+ map.addVLQMap(JSON.parse(result.map));
88
85
  asset.setMap(map);
89
86
  }
90
87
  } catch (err) {
@@ -102,39 +99,94 @@ export default new Transformer({
102
99
  asset.setCode(css);
103
100
  return [asset];
104
101
  },
105
- });
106
-
107
- function resolvePathImporter({resolve}) {
108
- return function(rawUrl, prev, done) {
109
- let url = rawUrl.replace(/^file:\/\//, '');
102
+ }): Transformer);
103
+
104
+ function resolvePathImporter({asset, resolve, includePaths, options}) {
105
+ // This is a reimplementation of the Sass resolution algorithm that uses Parcel's
106
+ // FS and tracks all tried files so they are watched for creation.
107
+ async function resolvePath(
108
+ url,
109
+ prev,
110
+ ): Promise<{filePath: string, contents: string, ...} | void> {
111
+ /*
112
+ Imports are resolved by trying, in order:
113
+ * Loading a file relative to the file in which the `@import` appeared.
114
+ * Each custom importer.
115
+ * Loading a file relative to the current working directory (This rule doesn't really make sense for Parcel).
116
+ * Each load path in `includePaths`
117
+ * Each load path specified in the `SASS_PATH` environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
118
+
119
+ See: https://sass-lang.com/documentation/js-api#importer
120
+ See also: https://github.com/sass/dart-sass/blob/006e6aa62f2417b5267ad5cdb5ba050226fab511/lib/src/importer/node/implementation.dart
121
+ */
122
+
123
+ let paths = [path.dirname(prev)];
124
+ if (includePaths) {
125
+ paths.push(...includePaths);
126
+ }
110
127
 
111
- if (WEBPACK_ALIAS_RE.test(url)) {
112
- const correctPath = url.replace(/^~/, '');
113
- const error = new Error(
114
- `The @import path "${url}" is using webpack specific syntax, which isn't supported by Parcel.\n\nTo @import files from node_modules, use "${correctPath}"`,
128
+ asset.invalidateOnEnvChange('SASS_PATH');
129
+ if (options.env.SASS_PATH) {
130
+ paths.push(
131
+ ...options.env.SASS_PATH.split(
132
+ process.platform === 'win32' ? ';' : ':',
133
+ ).map(p => path.resolve(options.projectRoot, p)),
115
134
  );
116
- done(error);
117
- return;
118
135
  }
119
136
 
120
- resolve(prev, url)
121
- .then(resolvedPath => {
122
- done({file: resolvedPath});
137
+ const urls = [url];
138
+ const urlFileName = path.basename(url);
139
+ if (urlFileName[0] !== '_') {
140
+ urls.push(path.join(path.dirname(url), `_${urlFileName}`));
141
+ }
142
+
143
+ if (url[0] !== '~') {
144
+ for (let p of paths) {
145
+ for (let u of urls) {
146
+ const filePath = path.resolve(p, u);
147
+ try {
148
+ const contents = await asset.fs.readFile(filePath, 'utf8');
149
+ return {
150
+ filePath,
151
+ contents,
152
+ };
153
+ } catch (err) {
154
+ asset.invalidateOnFileCreate({filePath});
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ // If none of the default sass rules apply, try Parcel's resolver.
161
+ for (let u of urls) {
162
+ if (NODE_MODULE_ALIAS_RE.test(u)) {
163
+ u = u.slice(1);
164
+ }
165
+ try {
166
+ const filePath = await resolve(prev, u);
167
+ if (filePath) {
168
+ const contents = await asset.fs.readFile(filePath, 'utf8');
169
+ return {filePath, contents};
170
+ }
171
+ } catch (err) {
172
+ continue;
173
+ }
174
+ }
175
+ }
176
+
177
+ return function (rawUrl, prev, done) {
178
+ const url = rawUrl.replace(/^file:\/\//, '');
179
+ resolvePath(url, prev)
180
+ .then(resolved => {
181
+ if (resolved) {
182
+ done({
183
+ file: resolved.filePath,
184
+ contents: resolved.contents,
185
+ });
186
+ } else {
187
+ done();
188
+ }
123
189
  })
124
- .catch(() => {
125
- /*
126
- We return `null` instead of an error so that Sass' resolution algorithm can continue.
127
-
128
- Imports are resolved by trying, in order:
129
- * Loading a file relative to the file in which the `@import` appeared.
130
- * Each custom importer.
131
- * Loading a file relative to the current working directory.
132
- * Each load path in `includePaths`
133
- * Each load path specified in the `SASS_PATH` environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
134
-
135
- See: https://sass-lang.com/documentation/js-api#importer
136
- */
137
- done(null);
138
- });
190
+ .catch(done);
139
191
  };
140
192
  }