@parcel/transformer-sass 2.0.0-nightly.97 → 2.0.0-nightly.982

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,85 +5,140 @@ 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
+ };
11
14
 
12
- var _path = require("path");
15
+ return data;
16
+ }
13
17
 
14
- var _os = require("os");
18
+ function _path() {
19
+ const data = _interopRequireDefault(require("path"));
15
20
 
16
- var _fs = require("@parcel/fs");
21
+ _path = function () {
22
+ return data;
23
+ };
17
24
 
18
- // E.g: ~library/file.sass
19
- const WEBPACK_ALIAS_RE = /^~[^/]/;
20
- const fs = new _fs.NodeFS();
21
- let didWarnAboutNodeSass = false;
25
+ return data;
26
+ }
22
27
 
23
- async function warnAboutNodeSassBeingUnsupported(filePath, logger) {
24
- if (!didWarnAboutNodeSass) {
25
- try {
26
- // TODO: replace this with the actual filesystem later
27
- await (0, _utils.resolve)(fs, 'node-sass', {
28
- basedir: (0, _path.dirname)(filePath)
29
- });
30
- logger.warn({
31
- origin: '@parcel/transformer-sass',
32
- message: '`node-sass` is unsupported in Parcel 2, it will use Dart Sass a.k.a. `sass`'
33
- });
34
- } catch (err) {
35
- if (err.code !== 'MODULE_NOT_FOUND') {
36
- throw err;
37
- }
38
- } finally {
39
- didWarnAboutNodeSass = true;
40
- }
41
- }
28
+ function _os() {
29
+ const data = require("os");
30
+
31
+ _os = function () {
32
+ return data;
33
+ };
34
+
35
+ return data;
42
36
  }
43
37
 
44
- var _default = new _plugin.Transformer({
45
- async getConfig({
46
- asset,
47
- resolve
38
+ function _sourceMap() {
39
+ const data = _interopRequireDefault(require("@parcel/source-map"));
40
+
41
+ _sourceMap = function () {
42
+ return data;
43
+ };
44
+
45
+ return data;
46
+ }
47
+
48
+ function _sass() {
49
+ const data = _interopRequireDefault(require("sass"));
50
+
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
+ }
67
+
68
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
69
+
70
+ // E.g: ~library/file.sass
71
+ const NODE_MODULE_ALIAS_RE = /^~[^/\\]/;
72
+
73
+ var _default = new (_plugin().Transformer)({
74
+ async loadConfig({
75
+ config,
76
+ options
48
77
  }) {
49
- let config = await asset.getConfig(['.sassrc', '.sassrc.js'], {
78
+ let configFile = await config.getConfig(['.sassrc', '.sassrc.json', '.sassrc.js'], {
50
79
  packageKey: 'sass'
51
80
  });
81
+ let configResult = configFile ? configFile.contents : {}; // Resolve relative paths from config file
52
82
 
53
- if (config === null) {
54
- config = {};
83
+ if (configFile && configResult.includePaths) {
84
+ configResult.includePaths = configResult.includePaths.map(p => _path().default.resolve(_path().default.dirname(configFile.filePath), p));
55
85
  }
56
86
 
57
- const code = await asset.getCode();
58
- config.data = config.data ? config.data + _os.EOL + code : code;
59
- config.file = asset.filePath;
60
-
61
- if (config.importer === undefined) {
62
- config.importer = [];
63
- } else if (!Array.isArray(config.importer)) {
64
- config.importer = [config.importer];
87
+ if (configFile && _path().default.extname(configFile.filePath) === '.js') {
88
+ config.invalidateOnStartup();
65
89
  }
66
90
 
67
- config.importer = [...config.importer, resolvePathImporter({
68
- resolve
69
- })];
70
- config.indentedSyntax = typeof config.indentedSyntax === 'boolean' ? config.indentedSyntax : asset.type === 'sass';
71
- return config;
91
+ if (configResult.importer === undefined) {
92
+ configResult.importer = [];
93
+ } else if (!Array.isArray(configResult.importer)) {
94
+ configResult.importer = [configResult.importer];
95
+ } // Always emit sourcemap
96
+
97
+
98
+ configResult.sourceMap = true; // sources are created relative to the directory of outFile
99
+
100
+ configResult.outFile = _path().default.join(options.projectRoot, 'style.css.map');
101
+ configResult.omitSourceMapUrl = true;
102
+ configResult.sourceMapContents = false;
103
+ return configResult;
72
104
  },
73
105
 
74
106
  async transform({
75
107
  asset,
76
108
  options,
77
109
  config,
78
- logger
110
+ resolve
79
111
  }) {
80
- await warnAboutNodeSassBeingUnsupported(asset.filePath, logger);
81
- let sass = await options.packageManager.require('sass', asset.filePath);
82
- 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));
83
114
  let css;
84
115
 
85
116
  try {
86
- css = (await sassRender(config)).css;
117
+ let code = await asset.getCode();
118
+ let result = await sassRender({ ...rawConfig,
119
+ file: asset.filePath,
120
+ data: rawConfig.data ? rawConfig.data + _os().EOL + code : code,
121
+ importer: [...rawConfig.importer, resolvePathImporter({
122
+ asset,
123
+ resolve,
124
+ includePaths: rawConfig.includePaths,
125
+ options
126
+ })],
127
+ indentedSyntax: typeof rawConfig.indentedSyntax === 'boolean' ? rawConfig.indentedSyntax : asset.type === 'sass'
128
+ });
129
+ css = result.css;
130
+
131
+ for (let included of result.stats.includedFiles) {
132
+ if (included !== asset.filePath) {
133
+ asset.invalidateOnFileChange(included);
134
+ }
135
+ }
136
+
137
+ if (result.map != null) {
138
+ let map = new (_sourceMap().default)(options.projectRoot);
139
+ map.addVLQMap(JSON.parse(result.map));
140
+ asset.setMap(map);
141
+ }
87
142
  } catch (err) {
88
143
  // Adapt the Error object for the reporter.
89
144
  err.fileName = err.file;
@@ -104,34 +159,97 @@ var _default = new _plugin.Transformer({
104
159
  exports.default = _default;
105
160
 
106
161
  function resolvePathImporter({
107
- resolve
162
+ asset,
163
+ resolve,
164
+ includePaths,
165
+ options
108
166
  }) {
109
- return function (rawUrl, prev, done) {
110
- 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
+ }
111
185
 
112
- if (WEBPACK_ALIAS_RE.test(url)) {
113
- const correctPath = url.replace(/^~/, '');
114
- 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}"`);
115
- done(error);
116
- 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)));
117
190
  }
118
191
 
119
- resolve(prev, url).then(resolvedPath => {
120
- done({
121
- file: resolvedPath
122
- });
123
- }).catch(() => {
124
- /*
125
- We return `null` instead of an error so that Sass' resolution algorithm can continue.
126
- Imports are resolved by trying, in order:
127
- * Loading a file relative to the file in which the `@import` appeared.
128
- * Each custom importer.
129
- * Loading a file relative to the current working directory.
130
- * Each load path in `includePaths`
131
- * Each load path specified in the `SASS_PATH` environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
132
- See: https://sass-lang.com/documentation/js-api#importer
133
- */
134
- done(null);
135
- });
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);
136
254
  };
137
255
  }
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@parcel/transformer-sass",
3
- "version": "2.0.0-nightly.97+a63f3fc9",
3
+ "version": "2.0.0-nightly.982+4745cd30",
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,16 +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.980+4745cd30"
17
21
  },
18
22
  "dependencies": {
19
- "@parcel/fs": "2.0.0-nightly.97+a63f3fc9",
20
- "@parcel/plugin": "2.0.0-nightly.97+a63f3fc9",
21
- "@parcel/utils": "2.0.0-nightly.97+a63f3fc9"
22
- },
23
- "devDependencies": {
24
- "sass": "^1.22.9"
23
+ "@parcel/plugin": "2.0.0-nightly.982+4745cd30",
24
+ "@parcel/source-map": "^2.0.0",
25
+ "sass": "^1.38.0"
25
26
  },
26
- "gitHead": "a63f3fc9726483219412920faeb255e035f90747"
27
+ "gitHead": "4745cd3023f8d5a5adcf9e565d5b82d1418dc262"
27
28
  }
@@ -1,79 +1,89 @@
1
1
  // @flow
2
- import type {PluginLogger} from '@parcel/logger';
3
-
4
2
  import {Transformer} from '@parcel/plugin';
5
- import {promisify, resolve} from '@parcel/utils';
6
- import {dirname} from 'path';
3
+ import path from 'path';
7
4
  import {EOL} from 'os';
8
- import {NodeFS} from '@parcel/fs';
5
+ import SourceMap from '@parcel/source-map';
6
+ import sass from 'sass';
7
+ import {promisify} from 'util';
9
8
 
10
9
  // E.g: ~library/file.sass
11
- const WEBPACK_ALIAS_RE = /^~[^/]/;
12
- const fs = new NodeFS();
13
-
14
- let didWarnAboutNodeSass = false;
15
-
16
- async function warnAboutNodeSassBeingUnsupported(
17
- filePath,
18
- logger: PluginLogger,
19
- ) {
20
- if (!didWarnAboutNodeSass) {
21
- try {
22
- // TODO: replace this with the actual filesystem later
23
- await resolve(fs, 'node-sass', {basedir: dirname(filePath)});
24
- logger.warn({
25
- origin: '@parcel/transformer-sass',
26
- message:
27
- '`node-sass` is unsupported in Parcel 2, it will use Dart Sass a.k.a. `sass`',
28
- });
29
- } catch (err) {
30
- if (err.code !== 'MODULE_NOT_FOUND') {
31
- throw err;
32
- }
33
- } finally {
34
- didWarnAboutNodeSass = true;
10
+ const NODE_MODULE_ALIAS_RE = /^~[^/\\]/;
11
+
12
+ export default (new Transformer({
13
+ async loadConfig({config, options}) {
14
+ let configFile = await config.getConfig(
15
+ ['.sassrc', '.sassrc.json', '.sassrc.js'],
16
+ {
17
+ packageKey: 'sass',
18
+ },
19
+ );
20
+
21
+ let configResult = configFile ? configFile.contents : {};
22
+
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
+ );
35
28
  }
36
- }
37
- }
38
29
 
39
- export default new Transformer({
40
- async getConfig({asset, resolve}) {
41
- let config = await asset.getConfig(['.sassrc', '.sassrc.js'], {
42
- packageKey: 'sass',
43
- });
44
-
45
- if (config === null) {
46
- config = {};
30
+ if (configFile && path.extname(configFile.filePath) === '.js') {
31
+ config.invalidateOnStartup();
47
32
  }
48
33
 
49
- const code = await asset.getCode();
50
- config.data = config.data ? config.data + EOL + code : code;
51
- config.file = asset.filePath;
52
-
53
- if (config.importer === undefined) {
54
- config.importer = [];
55
- } else if (!Array.isArray(config.importer)) {
56
- config.importer = [config.importer];
34
+ if (configResult.importer === undefined) {
35
+ configResult.importer = [];
36
+ } else if (!Array.isArray(configResult.importer)) {
37
+ configResult.importer = [configResult.importer];
57
38
  }
58
39
 
59
- config.importer = [...config.importer, resolvePathImporter({resolve})];
40
+ // Always emit sourcemap
41
+ configResult.sourceMap = true;
42
+ // sources are created relative to the directory of outFile
43
+ configResult.outFile = path.join(options.projectRoot, 'style.css.map');
44
+ configResult.omitSourceMapUrl = true;
45
+ configResult.sourceMapContents = false;
60
46
 
61
- config.indentedSyntax =
62
- typeof config.indentedSyntax === 'boolean'
63
- ? config.indentedSyntax
64
- : asset.type === 'sass';
65
-
66
- return config;
47
+ return configResult;
67
48
  },
68
49
 
69
- async transform({asset, options, config, logger}) {
70
- await warnAboutNodeSassBeingUnsupported(asset.filePath, logger);
71
- let sass = await options.packageManager.require('sass', asset.filePath);
72
- const sassRender = promisify(sass.render.bind(sass));
73
-
50
+ async transform({asset, options, config, resolve}) {
51
+ let rawConfig = config ?? {};
52
+ let sassRender = promisify(sass.render.bind(sass));
74
53
  let css;
75
54
  try {
76
- css = (await sassRender(config)).css;
55
+ let code = await asset.getCode();
56
+ let result = await sassRender({
57
+ ...rawConfig,
58
+ file: asset.filePath,
59
+ data: rawConfig.data ? rawConfig.data + EOL + code : code,
60
+ importer: [
61
+ ...rawConfig.importer,
62
+ resolvePathImporter({
63
+ asset,
64
+ resolve,
65
+ includePaths: rawConfig.includePaths,
66
+ options,
67
+ }),
68
+ ],
69
+ indentedSyntax:
70
+ typeof rawConfig.indentedSyntax === 'boolean'
71
+ ? rawConfig.indentedSyntax
72
+ : asset.type === 'sass',
73
+ });
74
+
75
+ css = result.css;
76
+ for (let included of result.stats.includedFiles) {
77
+ if (included !== asset.filePath) {
78
+ asset.invalidateOnFileChange(included);
79
+ }
80
+ }
81
+
82
+ if (result.map != null) {
83
+ let map = new SourceMap(options.projectRoot);
84
+ map.addVLQMap(JSON.parse(result.map));
85
+ asset.setMap(map);
86
+ }
77
87
  } catch (err) {
78
88
  // Adapt the Error object for the reporter.
79
89
  err.fileName = err.file;
@@ -89,39 +99,94 @@ export default new Transformer({
89
99
  asset.setCode(css);
90
100
  return [asset];
91
101
  },
92
- });
93
-
94
- function resolvePathImporter({resolve}) {
95
- return function(rawUrl, prev, done) {
96
- 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
+ }
97
127
 
98
- if (WEBPACK_ALIAS_RE.test(url)) {
99
- const correctPath = url.replace(/^~/, '');
100
- const error = new Error(
101
- `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)),
102
134
  );
103
- done(error);
104
- return;
105
135
  }
106
136
 
107
- resolve(prev, url)
108
- .then(resolvedPath => {
109
- 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
+ }
110
189
  })
111
- .catch(() => {
112
- /*
113
- We return `null` instead of an error so that Sass' resolution algorithm can continue.
114
-
115
- Imports are resolved by trying, in order:
116
- * Loading a file relative to the file in which the `@import` appeared.
117
- * Each custom importer.
118
- * Loading a file relative to the current working directory.
119
- * Each load path in `includePaths`
120
- * Each load path specified in the `SASS_PATH` environment variable, which should be semicolon-separated on Windows and colon-separated elsewhere.
121
-
122
- See: https://sass-lang.com/documentation/js-api#importer
123
- */
124
- done(null);
125
- });
190
+ .catch(done);
126
191
  };
127
192
  }