1 | import {Syntax} from './options';
|
2 | import {PromiseOr} from './util/promise_or';
|
3 |
|
4 | /**
|
5 | * Contextual information passed to {@link Importer.canonicalize} and {@link
|
6 | * FileImporter.findFileUrl}. Not all importers will need this information to
|
7 | * resolve loads, but some may find it useful.
|
8 | */
|
9 | export interface CanonicalizeContext {
|
10 | /**
|
11 | * Whether this is being invoked because of a Sass
|
12 | * `@import` rule, as opposed to a `@use` or `@forward` rule.
|
13 | *
|
14 | * This should *only* be used for determining whether or not to load
|
15 | * [import-only files](https://sass-lang.com/documentation/at-rules/import#import-only-files).
|
16 | */
|
17 | fromImport: boolean;
|
18 |
|
19 | /**
|
20 | * The canonical URL of the file that contains the load, if that information
|
21 | * is available.
|
22 | *
|
23 | * For an {@link Importer}, this is only passed when the `url` parameter is a
|
24 | * relative URL _or_ when its [URL scheme] is included in {@link
|
25 | * Importer.nonCanonicalScheme}. This ensures that canonical URLs are always
|
26 | * resolved the same way regardless of context.
|
27 | *
|
28 | * [URL scheme]: https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL#scheme
|
29 | *
|
30 | * For a {@link FileImporter}, this is always available as long as Sass knows
|
31 | * the canonical URL of the containing file.
|
32 | */
|
33 | containingUrl: URL | null;
|
34 | }
|
35 |
|
36 | /**
|
37 | * A special type of importer that redirects all loads to existing files on
|
38 | * disk. Although this is less powerful than a full {@link Importer}, it
|
39 | * automatically takes care of Sass features like resolving partials and file
|
40 | * extensions and of loading the file from disk.
|
41 | *
|
42 | * Like all importers, this implements custom Sass loading logic for [`@use`
|
43 | * rules](https://sass-lang.com/documentation/at-rules/use) and [`@import`
|
44 | * rules](https://sass-lang.com/documentation/at-rules/import). It can be passed
|
45 | * to {@link Options.importers} or {@link StringOptions.importer}.
|
46 | *
|
47 | * @typeParam sync - A `FileImporter<'sync'>`'s {@link findFileUrl} must return
|
48 | * synchronously, but in return it can be passed to {@link compile} and {@link
|
49 | * compileString} in addition to {@link compileAsync} and {@link
|
50 | * compileStringAsync}.
|
51 | *
|
52 | * A `FileImporter<'async'>`'s {@link findFileUrl} may either return
|
53 | * synchronously or asynchronously, but it can only be used with {@link
|
54 | * compileAsync} and {@link compileStringAsync}.
|
55 | *
|
56 | * @example
|
57 | *
|
58 | * ```js
|
59 | * const {pathToFileURL} = require('url');
|
60 | *
|
61 | * sass.compile('style.scss', {
|
62 | * importers: [{
|
63 | * // An importer that redirects relative URLs starting with "~" to
|
64 | * // `node_modules`.
|
65 | * findFileUrl(url) {
|
66 | * if (!url.startsWith('~')) return null;
|
67 | * return new URL(url.substring(1), pathToFileURL('node_modules'));
|
68 | * }
|
69 | * }]
|
70 | * });
|
71 | * ```
|
72 | *
|
73 | * @category Importer
|
74 | */
|
75 | export interface FileImporter<
|
76 | sync extends 'sync' | 'async' = 'sync' | 'async'
|
77 | > {
|
78 | /**
|
79 | * A callback that's called to partially resolve a load (such as
|
80 | * [`@use`](https://sass-lang.com/documentation/at-rules/use) or
|
81 | * [`@import`](https://sass-lang.com/documentation/at-rules/import)) to a file
|
82 | * on disk.
|
83 | *
|
84 | * Unlike an {@link Importer}, the compiler will automatically handle relative
|
85 | * loads for a {@link FileImporter}. See {@link Options.importers} for more
|
86 | * details on the way loads are resolved.
|
87 | *
|
88 | * @param url - The loaded URL. Since this might be relative, it's represented
|
89 | * as a string rather than a {@link URL} object.
|
90 | *
|
91 | * @returns An absolute `file:` URL if this importer recognizes the `url`.
|
92 | * This may be only partially resolved: the compiler will automatically look
|
93 | * for [partials](https://sass-lang.com/documentation/at-rules/use#partials),
|
94 | * [index files](https://sass-lang.com/documentation/at-rules/use#index-files),
|
95 | * and file extensions based on the returned URL. An importer may also return
|
96 | * a fully resolved URL if it so chooses.
|
97 | *
|
98 | * If this importer doesn't recognize the URL, it should return `null` instead
|
99 | * to allow other importers or {@link Options.loadPaths | load paths} to
|
100 | * handle it.
|
101 | *
|
102 | * This may also return a `Promise`, but if it does the importer may only be
|
103 | * passed to {@link compileAsync} and {@link compileStringAsync}, not {@link
|
104 | * compile} or {@link compileString}.
|
105 | *
|
106 | * @throws any - If this importer recognizes `url` but determines that it's
|
107 | * invalid, it may throw an exception that will be wrapped by Sass. If the
|
108 | * exception object has a `message` property, it will be used as the wrapped
|
109 | * exception's message; otherwise, the exception object's `toString()` will be
|
110 | * used. This means it's safe for importers to throw plain strings.
|
111 | */
|
112 | findFileUrl(
|
113 | url: string,
|
114 | context: CanonicalizeContext
|
115 | ): PromiseOr<URL | null, sync>;
|
116 |
|
117 | /** @hidden */
|
118 | canonicalize?: never;
|
119 | }
|
120 |
|
121 | /**
|
122 | * An object that implements custom Sass loading logic for [`@use`
|
123 | * rules](https://sass-lang.com/documentation/at-rules/use) and [`@import`
|
124 | * rules](https://sass-lang.com/documentation/at-rules/import). It can be passed
|
125 | * to {@link Options.importers} or {@link StringOptions.importer}.
|
126 | *
|
127 | * Importers that simply redirect to files on disk are encouraged to use the
|
128 | * {@link FileImporter} interface instead.
|
129 | *
|
130 | * ### Resolving a Load
|
131 | *
|
132 | * This is the process of resolving a load using a custom importer:
|
133 | *
|
134 | * - The compiler encounters `@use "db:foo/bar/baz"`.
|
135 | * - It calls {@link canonicalize} with `"db:foo/bar/baz"`.
|
136 | * - {@link canonicalize} returns `new URL("db:foo/bar/baz/_index.scss")`.
|
137 | * - If the compiler has already loaded a stylesheet with this canonical URL, it
|
138 | * re-uses the existing module.
|
139 | * - Otherwise, it calls {@link load} with `new
|
140 | * URL("db:foo/bar/baz/_index.scss")`.
|
141 | * - {@link load} returns an {@link ImporterResult} that the compiler uses as
|
142 | * the contents of the module.
|
143 | *
|
144 | * See {@link Options.importers} for more details on the way loads are resolved
|
145 | * using multiple importers and load paths.
|
146 | *
|
147 | * @typeParam sync - An `Importer<'sync'>`'s {@link canonicalize} and {@link
|
148 | * load} must return synchronously, but in return it can be passed to {@link
|
149 | * compile} and {@link compileString} in addition to {@link compileAsync} and
|
150 | * {@link compileStringAsync}.
|
151 | *
|
152 | * An `Importer<'async'>`'s {@link canonicalize} and {@link load} may either
|
153 | * return synchronously or asynchronously, but it can only be used with {@link
|
154 | * compileAsync} and {@link compileStringAsync}.
|
155 | *
|
156 | * @example
|
157 | *
|
158 | * ```js
|
159 | * sass.compile('style.scss', {
|
160 | * // An importer for URLs like `bgcolor:orange` that generates a
|
161 | * // stylesheet with the given background color.
|
162 | * importers: [{
|
163 | * canonicalize(url) {
|
164 | * if (!url.startsWith('bgcolor:')) return null;
|
165 | * return new URL(url);
|
166 | * },
|
167 | * load(canonicalUrl) {
|
168 | * return {
|
169 | * contents: `body {background-color: ${canonicalUrl.pathname}}`,
|
170 | * syntax: 'scss'
|
171 | * };
|
172 | * }
|
173 | * }]
|
174 | * });
|
175 | * ```
|
176 | *
|
177 | * @category Importer
|
178 | */
|
179 | export interface Importer<sync extends 'sync' | 'async' = 'sync' | 'async'> {
|
180 | /**
|
181 | * If `url` is recognized by this importer, returns its canonical format.
|
182 | *
|
183 | * If Sass has already loaded a stylesheet with the returned canonical URL, it
|
184 | * re-uses the existing parse tree (and the loaded module for `@use`). This
|
185 | * means that importers **must ensure** that the same canonical URL always
|
186 | * refers to the same stylesheet, *even across different importers*. As such,
|
187 | * importers are encouraged to use unique URL schemes to disambiguate between
|
188 | * one another.
|
189 | *
|
190 | * As much as possible, custom importers should canonicalize URLs the same way
|
191 | * as the built-in filesystem importer:
|
192 | *
|
193 | * - The importer should look for stylesheets by adding the prefix `_` to the
|
194 | * URL's basename, and by adding the extensions `.sass` and `.scss` if the
|
195 | * URL doesn't already have one of those extensions. For example, if the
|
196 | * URL was `foo/bar/baz`, the importer would look for:
|
197 | * - `foo/bar/baz.sass`
|
198 | * - `foo/bar/baz.scss`
|
199 | * - `foo/bar/_baz.sass`
|
200 | * - `foo/bar/_baz.scss`
|
201 | *
|
202 | * If the URL was `foo/bar/baz.scss`, the importer would just look for:
|
203 | * - `foo/bar/baz.scss`
|
204 | * - `foo/bar/_baz.scss`
|
205 | *
|
206 | * If the importer finds a stylesheet at more than one of these URLs, it
|
207 | * should throw an exception indicating that the URL is ambiguous. Note that
|
208 | * if the extension is explicitly specified, a stylesheet with the opposite
|
209 | * extension is allowed to exist.
|
210 | *
|
211 | * - If none of the possible paths is valid, the importer should perform the
|
212 | * same resolution on the URL followed by `/index`. In the example above,
|
213 | * it would look for:
|
214 | * - `foo/bar/baz/index.sass`
|
215 | * - `foo/bar/baz/index.scss`
|
216 | * - `foo/bar/baz/_index.sass`
|
217 | * - `foo/bar/baz/_index.scss`
|
218 | *
|
219 | * As above, if the importer finds a stylesheet at more than one of these
|
220 | * URLs, it should throw an exception indicating that the import is
|
221 | * ambiguous.
|
222 | *
|
223 | * If no stylesheets are found, the importer should return `null`.
|
224 | *
|
225 | * Calling {@link canonicalize} multiple times with the same URL must return
|
226 | * the same result. Calling {@link canonicalize} with a URL returned by a
|
227 | * previous call to {@link canonicalize} must return that URL.
|
228 | *
|
229 | * Relative loads in stylesheets loaded from an importer are handled by
|
230 | * resolving the loaded URL relative to the canonical URL of the stylesheet
|
231 | * that contains it, and passing that URL back to the importer's {@link
|
232 | * canonicalize} method. For example, suppose the "Resolving a Load" example
|
233 | * {@link Importer | above} returned a stylesheet that contained `@use
|
234 | * "mixins"`:
|
235 | *
|
236 | * - The compiler resolves the URL `mixins` relative to the current
|
237 | * stylesheet's canonical URL `db:foo/bar/baz/_index.scss` to get
|
238 | * `db:foo/bar/baz/mixins`.
|
239 | * - It calls {@link canonicalize} with `"db:foo/bar/baz/mixins"`.
|
240 | * - {@link canonicalize} returns `new URL("db:foo/bar/baz/_mixins.scss")`.
|
241 | *
|
242 | * Because of this, {@link canonicalize} must return a meaningful result when
|
243 | * called with a URL relative to one returned by an earlier call to {@link
|
244 | * canonicalize}.
|
245 | *
|
246 | * @param url - The loaded URL. Since this might be relative, it's represented
|
247 | * as a string rather than a {@link URL} object.
|
248 | *
|
249 | * @returns An absolute URL if this importer recognizes the `url`, or `null`
|
250 | * if it doesn't. If this returns `null`, other importers or {@link
|
251 | * Options.loadPaths | load paths} may handle the load.
|
252 | *
|
253 | * This may also return a `Promise`, but if it does the importer may only be
|
254 | * passed to {@link compileAsync} and {@link compileStringAsync}, not {@link
|
255 | * compile} or {@link compileString}.
|
256 | *
|
257 | * @throws any - If this importer recognizes `url` but determines that it's
|
258 | * invalid, it may throw an exception that will be wrapped by Sass. If the
|
259 | * exception object has a `message` property, it will be used as the wrapped
|
260 | * exception's message; otherwise, the exception object's `toString()` will be
|
261 | * used. This means it's safe for importers to throw plain strings.
|
262 | */
|
263 | canonicalize(
|
264 | url: string,
|
265 | context: CanonicalizeContext
|
266 | ): PromiseOr<URL | null, sync>;
|
267 |
|
268 | /**
|
269 | * Loads the Sass text for the given `canonicalUrl`, or returns `null` if this
|
270 | * importer can't find the stylesheet it refers to.
|
271 | *
|
272 | * @param canonicalUrl - The canonical URL of the stylesheet to load. This is
|
273 | * guaranteed to come from a call to {@link canonicalize}, although not every
|
274 | * call to {@link canonicalize} will result in a call to {@link load}.
|
275 | *
|
276 | * @returns The contents of the stylesheet at `canonicalUrl` if it can be
|
277 | * loaded, or `null` if it can't.
|
278 | *
|
279 | * This may also return a `Promise`, but if it does the importer may only be
|
280 | * passed to {@link compileAsync} and {@link compileStringAsync}, not {@link
|
281 | * compile} or {@link compileString}.
|
282 | *
|
283 | * @throws any - If this importer finds a stylesheet at `url` but it fails to
|
284 | * load for some reason, or if `url` is uniquely associated with this importer
|
285 | * but doesn't refer to a real stylesheet, the importer may throw an exception
|
286 | * that will be wrapped by Sass. If the exception object has a `message`
|
287 | * property, it will be used as the wrapped exception's message; otherwise,
|
288 | * the exception object's `toString()` will be used. This means it's safe for
|
289 | * importers to throw plain strings.
|
290 | */
|
291 | load(canonicalUrl: URL): PromiseOr<ImporterResult | null, sync>;
|
292 |
|
293 | /** @hidden */
|
294 | findFileUrl?: never;
|
295 |
|
296 | /**
|
297 | * A URL scheme or set of schemes (without the `:`) that this importer
|
298 | * promises never to use for URLs returned by {@link canonicalize}. If it does
|
299 | * return a URL with one of these schemes, that's an error.
|
300 | *
|
301 | * If this is set, any call to canonicalize for a URL with a non-canonical
|
302 | * scheme will be passed {@link CanonicalizeContext.containingUrl} if it's
|
303 | * known.
|
304 | *
|
305 | * These schemes may only contain lowercase ASCII letters, ASCII numerals,
|
306 | * `+`, `-`, and `.`. They may not be empty.
|
307 | */
|
308 | nonCanonicalScheme?: string | string[];
|
309 | }
|
310 |
|
311 | declare const nodePackageImporterKey: unique symbol;
|
312 |
|
313 | /**
|
314 | * The built-in Node.js package importer. This loads pkg: URLs from node_modules
|
315 | * according to the standard Node.js resolution algorithm.
|
316 | *
|
317 | * A Node.js package importer is exposed as a class that can be added to the
|
318 | * `importers` option.
|
319 | *
|
320 | *```js
|
321 | * const sass = require('sass');
|
322 | * sass.compileString('@use "pkg:vuetify', {
|
323 | * importers: [new sass.NodePackageImporter()]
|
324 | * });
|
325 | *```
|
326 | *
|
327 | * ## Writing Sass packages
|
328 | *
|
329 | * Package authors can control what is exposed to their users through their
|
330 | * `package.json` manifest. The recommended method is to add a `sass`
|
331 | * conditional export to `package.json`.
|
332 | *
|
333 | * ```json
|
334 | * // node_modules/uicomponents/package.json
|
335 | * {
|
336 | * "exports": {
|
337 | * ".": {
|
338 | * "sass": "./src/scss/index.scss",
|
339 | * "import": "./dist/js/index.mjs",
|
340 | * "default": "./dist/js/index.js"
|
341 | * }
|
342 | * }
|
343 | * }
|
344 | * ```
|
345 | *
|
346 | * This allows a package user to write `@use "pkg:uicomponents"` to load the
|
347 | * file at `node_modules/uicomponents/src/scss/index.scss`.
|
348 | *
|
349 | * The Node.js package importer supports the variety of formats supported by
|
350 | * Node.js [package entry points], allowing authors to expose multiple subpaths.
|
351 | *
|
352 | * [package entry points]:
|
353 | * https://nodejs.org/api/packages.html#package-entry-points
|
354 | *
|
355 | * ```json
|
356 | * // node_modules/uicomponents/package.json
|
357 | * {
|
358 | * "exports": {
|
359 | * ".": {
|
360 | * "sass": "./src/scss/index.scss",
|
361 | * },
|
362 | * "./colors.scss": {
|
363 | * "sass": "./src/scss/_colors.scss",
|
364 | * },
|
365 | * "./theme/*.scss": {
|
366 | * "sass": "./src/scss/theme/*.scss",
|
367 | * },
|
368 | * }
|
369 | * }
|
370 | * ```
|
371 | *
|
372 | * This allows a package user to write:
|
373 | *
|
374 | * - `@use "pkg:uicomponents";` to import the root export.
|
375 | * - `@use "pkg:uicomponents/colors";` to import the colors partial.
|
376 | * - `@use "pkg:uicomponents/theme/purple";` to import a purple theme.
|
377 | *
|
378 | * Note that while library users can rely on the importer to resolve
|
379 | * [partials](https://sass-lang.com/documentation/at-rules/use#partials), [index
|
380 | * files](https://sass-lang.com/documentation/at-rules/use#index-files), and
|
381 | * extensions, library authors must specify the entire file path in `exports`.
|
382 | *
|
383 | * In addition to the `sass` condition, the `style` condition is also
|
384 | * acceptable. Sass will match the `default` condition if it's a relevant file
|
385 | * type, but authors are discouraged from relying on this. Notably, the key
|
386 | * order matters, and the importer will resolve to the first value with a key
|
387 | * that is `sass`, `style`, or `default`, so you should always put `default`
|
388 | * last.
|
389 | *
|
390 | * To help package authors who haven't transitioned to package entry points
|
391 | * using the `exports` field, the Node.js package importer provides several
|
392 | * fallback options. If the `pkg:` URL does not have a subpath, the Node.js
|
393 | * package importer will look for a `sass` or `style` key at the root of
|
394 | * `package.json`.
|
395 | *
|
396 | * ```json
|
397 | * // node_modules/uicomponents/package.json
|
398 | * {
|
399 | * "sass": "./src/scss/index.scss",
|
400 | * }
|
401 | * ```
|
402 | *
|
403 | * This allows a user to write `@use "pkg:uicomponents";` to import the
|
404 | * `index.scss` file.
|
405 | *
|
406 | * Finally, the Node.js package importer will look for an `index` file at the
|
407 | * package root, resolving partials and extensions. For example, if the file
|
408 | * `_index.scss` exists in the package root of `uicomponents`, a user can import
|
409 | * that with `@use "pkg:uicomponents";`.
|
410 | *
|
411 | * If a `pkg:` URL includes a subpath that doesn't have a match in package entry
|
412 | * points, the Node.js importer will attempt to find that file relative to the
|
413 | * package root, resolving for file extensions, partials and index files. For
|
414 | * example, if the file `src/sass/_colors.scss` exists in the `uicomponents`
|
415 | * package, a user can import that file using `@use
|
416 | * "pkg:uicomponents/src/sass/colors";`.
|
417 | *
|
418 | * @compatibility dart: "1.71.0", node: false
|
419 | * @category Importer
|
420 | */
|
421 | export class NodePackageImporter {
|
422 | /** Used to distinguish this type from any arbitrary object. */
|
423 | private readonly [nodePackageImporterKey]: true;
|
424 |
|
425 | /**
|
426 | * The NodePackageImporter has an optional `entryPointDirectory` option, which
|
427 | * is the directory where the Node Package Importer should start when
|
428 | * resolving `pkg:` URLs in sources other than files on disk. This will be
|
429 | * used as the `parentURL` in the [Node Module
|
430 | * Resolution](https://nodejs.org/api/esm.html#resolution-algorithm-specification)
|
431 | * algorithm.
|
432 | *
|
433 | * In order to be found by the Node Package Importer, a package will need to
|
434 | * be inside a node_modules folder located in the `entryPointDirectory`, or
|
435 | * one of its parent directories, up to the filesystem root.
|
436 | *
|
437 | * Relative paths will be resolved relative to the current working directory.
|
438 | * If a path is not provided, this defaults to the parent directory of the
|
439 | * Node.js entrypoint. If that's not available, this will throw an error.
|
440 | */
|
441 | constructor(entryPointDirectory?: string);
|
442 | }
|
443 |
|
444 | /**
|
445 | * The result of successfully loading a stylesheet with an { Importer}.
|
446 | *
|
447 | * Importer
|
448 | */
|
449 | export interface ImporterResult {
|
450 | /** The contents of the stylesheet. */
|
451 | contents: string;
|
452 |
|
453 | /** The syntax with which to parse {@link contents}. */
|
454 | syntax: Syntax;
|
455 |
|
456 | /**
|
457 | * The URL to use to link to the loaded stylesheet's source code in source
|
458 | * maps. A `file:` URL is ideal because it's accessible to both browsers and
|
459 | * other build tools, but an `http:` URL is also acceptable.
|
460 | *
|
461 | * If this isn't set, it defaults to a `data:` URL that contains the contents
|
462 | * of the loaded stylesheet.
|
463 | */
|
464 | sourceMapUrl?: URL;
|
465 | }
|