1 | import {Syntax} from './options';
|
2 | import {PromiseOr} from './util/promise_or';
|
3 |
|
4 | /**
|
5 | * A special type of importer that redirects all loads to existing files on
|
6 | * disk. Although this is less powerful than a full [[Importer]], it
|
7 | * automatically takes care of Sass features like resolving partials and file
|
8 | * extensions and of loading the file from disk.
|
9 | *
|
10 | * Like all importers, this implements custom Sass loading logic for [`@use`
|
11 | * rules](https://sass-lang.com/documentation/at-rules/use) and [`@import`
|
12 | * rules](https://sass-lang.com/documentation/at-rules/import). It can be passed
|
13 | * to [[Options.importers]] or [[StringOptionsWithImporter.importer]].
|
14 | *
|
15 | * @typeParam sync - A `FileImporter<'sync'>`'s [[findFileUrl]] must return
|
16 | * synchronously, but in return it can be passed to [[compile]] and
|
17 | * [[compileString]] in addition to [[compileAsync]] and [[compileStringAsync]].
|
18 | *
|
19 | * A `FileImporter<'async'>`'s [[findFileUrl]] may either return synchronously
|
20 | * or asynchronously, but it can only be used with [[compileAsync]] and
|
21 | * [[compileStringAsync]].
|
22 | *
|
23 | * @example
|
24 | *
|
25 | * ```js
|
26 | * const {pathToFileURL} = require('url');
|
27 | *
|
28 | * sass.compile('style.scss', {
|
29 | * importers: [{
|
30 | * // An importer that redirects relative URLs starting with "~" to
|
31 | * // `node_modules`.
|
32 | * findFileUrl(url) {
|
33 | * if (!url.startsWith('~')) return null;
|
34 | * return new URL(url.substring(1), pathToFileURL('node_modules'));
|
35 | * }
|
36 | * }]
|
37 | * });
|
38 | * ```
|
39 | *
|
40 | * @category Importer
|
41 | */
|
42 | export interface FileImporter<
|
43 | sync extends 'sync' | 'async' = 'sync' | 'async'
|
44 | > {
|
45 | /**
|
46 | * A callback that's called to partially resolve a load (such as
|
47 | * [`@use`](https://sass-lang.com/documentation/at-rules/use) or
|
48 | * [`@import`](https://sass-lang.com/documentation/at-rules/import)) to a file
|
49 | * on disk.
|
50 | *
|
51 | * Unlike an [[Importer]], the compiler will automatically handle relative
|
52 | * loads for a [[FileImporter]]. See [[Options.importers]] for more details on
|
53 | * the way loads are resolved.
|
54 | *
|
55 | * @param url - The loaded URL. Since this might be relative, it's represented
|
56 | * as a string rather than a [[URL]] object.
|
57 | *
|
58 | * @param options.fromImport - Whether this is being invoked because of a Sass
|
59 | * `@import` rule, as opposed to a `@use` or `@forward` rule.
|
60 | *
|
61 | * This should *only* be used for determining whether or not to load
|
62 | * [import-only files](https://sass-lang.com/documentation/at-rules/import#import-only-files).
|
63 | *
|
64 | * @returns An absolute `file:` URL if this importer recognizes the `url`.
|
65 | * This may be only partially resolved: the compiler will automatically look
|
66 | * for [partials](https://sass-lang.com/documentation/at-rules/use#partials),
|
67 | * [index files](https://sass-lang.com/documentation/at-rules/use#index-files),
|
68 | * and file extensions based on the returned URL. An importer may also return
|
69 | * a fully resolved URL if it so chooses.
|
70 | *
|
71 | * If this importer doesn't recognize the URL, it should return `null` instead
|
72 | * to allow other importers or {@link Options.loadPaths | load paths} to
|
73 | * handle it.
|
74 | *
|
75 | * This may also return a `Promise`, but if it does the importer may only be
|
76 | * passed to [[compileAsync]] and [[compileStringAsync]], not [[compile]] or
|
77 | * [[compileString]].
|
78 | *
|
79 | * @throws any - If this importer recognizes `url` but determines that it's
|
80 | * invalid, it may throw an exception that will be wrapped by Sass. If the
|
81 | * exception object has a `message` property, it will be used as the wrapped
|
82 | * exception's message; otherwise, the exception object's `toString()` will be
|
83 | * used. This means it's safe for importers to throw plain strings.
|
84 | */
|
85 | findFileUrl(
|
86 | url: string,
|
87 | options: {fromImport: boolean}
|
88 | ): PromiseOr<URL | null, sync>;
|
89 |
|
90 | /** @hidden */
|
91 | canonicalize?: never;
|
92 | }
|
93 |
|
94 | /**
|
95 | * An object that implements custom Sass loading logic for [`@use`
|
96 | * rules](https://sass-lang.com/documentation/at-rules/use) and [`@import`
|
97 | * rules](https://sass-lang.com/documentation/at-rules/import). It can be passed
|
98 | * to [[Options.importers]] or [[StringOptionsWithImporter.importer]].
|
99 | *
|
100 | * Importers that simply redirect to files on disk are encouraged to use the
|
101 | * [[FileImporter]] interface instead.
|
102 | *
|
103 | * See [[Options.importers]] for more details on the way loads are resolved.
|
104 | *
|
105 | * @typeParam sync - An `Importer<'sync'>`'s [[canonicalize]] and [[load]] must
|
106 | * return synchronously, but in return it can be passed to [[compile]] and
|
107 | * [[compileString]] in addition to [[compileAsync]] and [[compileStringAsync]].
|
108 | *
|
109 | * An `Importer<'async'>`'s [[canonicalize]] and [[load]] may either return
|
110 | * synchronously or asynchronously, but it can only be used with
|
111 | * [[compileAsync]] and [[compileStringAsync]].
|
112 | *
|
113 | * @example Resolving a Load
|
114 | *
|
115 | * This is the process of resolving a load using a custom importer:
|
116 | *
|
117 | * - The compiler encounters `@use "db:foo/bar/baz"`.
|
118 | * - It calls [[canonicalize]] with `"db:foo/bar/baz"`.
|
119 | * - [[canonicalize]] returns `new URL("db:foo/bar/baz/_index.scss")`.
|
120 | * - If the compiler has already loaded a stylesheet with this canonical URL, it
|
121 | * re-uses the existing module.
|
122 | * - Otherwise, it calls [[load]] with `new URL("db:foo/bar/baz/_index.scss")`.
|
123 | * - [[load]] returns an [[ImporterResult]] that the compiler uses as the
|
124 | * contents of the module.
|
125 | *
|
126 | * @example Code Sample
|
127 | *
|
128 | * ```js
|
129 | * sass.compile('style.scss', {
|
130 | * // An importer for URLs like `bgcolor:orange` that generates a
|
131 | * // stylesheet with the given background color.
|
132 | * importers: [{
|
133 | * canonicalize(url) {
|
134 | * if (!url.startsWith('bgcolor:')) return null;
|
135 | * return new URL(url);
|
136 | * },
|
137 | * load(canonicalUrl) {
|
138 | * return {
|
139 | * contents: `body {background-color: ${canonicalUrl.pathname}}`,
|
140 | * syntax: 'scss'
|
141 | * };
|
142 | * }
|
143 | * }]
|
144 | * });
|
145 | * ```
|
146 | *
|
147 | * @category Importer
|
148 | */
|
149 | export interface Importer<sync extends 'sync' | 'async' = 'sync' | 'async'> {
|
150 | /**
|
151 | * If `url` is recognized by this importer, returns its canonical format.
|
152 | *
|
153 | * If Sass has already loaded a stylesheet with the returned canonical URL, it
|
154 | * re-uses the existing parse tree (and the loaded module for `@use`). This
|
155 | * means that importers **must ensure** that the same canonical URL always
|
156 | * refers to the same stylesheet, *even across different importers*. As such,
|
157 | * importers are encouraged to use unique URL schemes to disambiguate between
|
158 | * one another.
|
159 | *
|
160 | * As much as possible, custom importers should canonicalize URLs the same way
|
161 | * as the built-in filesystem importer:
|
162 | *
|
163 | * - The importer should look for stylesheets by adding the prefix `_` to the
|
164 | * URL's basename, and by adding the extensions `.sass` and `.scss` if the
|
165 | * URL doesn't already have one of those extensions. For example, if the
|
166 | * URL was `foo/bar/baz`, the importer would look for:
|
167 | * - `foo/bar/baz.sass`
|
168 | * - `foo/bar/baz.scss`
|
169 | * - `foo/bar/_baz.sass`
|
170 | * - `foo/bar/_baz.scss`
|
171 | *
|
172 | * If the URL was `foo/bar/baz.scss`, the importer would just look for:
|
173 | * - `foo/bar/baz.scss`
|
174 | * - `foo/bar/_baz.scss`
|
175 | *
|
176 | * If the importer finds a stylesheet at more than one of these URLs, it
|
177 | * should throw an exception indicating that the URL is ambiguous. Note that
|
178 | * if the extension is explicitly specified, a stylesheet with the opposite
|
179 | * extension is allowed to exist.
|
180 | *
|
181 | * - If none of the possible paths is valid, the importer should perform the
|
182 | * same resolution on the URL followed by `/index`. In the example above,
|
183 | * it would look for:
|
184 | * - `foo/bar/baz/index.sass`
|
185 | * - `foo/bar/baz/index.scss`
|
186 | * - `foo/bar/baz/_index.sass`
|
187 | * - `foo/bar/baz/_index.scss`
|
188 | *
|
189 | * As above, if the importer finds a stylesheet at more than one of these
|
190 | * URLs, it should throw an exception indicating that the import is
|
191 | * ambiguous.
|
192 | *
|
193 | * If no stylesheets are found, the importer should return `null`.
|
194 | *
|
195 | * Calling [[canonicalize]] multiple times with the same URL must return the
|
196 | * same result. Calling [[canonicalize]] with a URL returned by a previous
|
197 | * call to [[canonicalize]] must return that URL.
|
198 | *
|
199 | * Relative loads in stylesheets loaded from an importer are handled by
|
200 | * resolving the loaded URL relative to the canonical URL of the stylesheet
|
201 | * that contains it, and passing that URL back to the importer's
|
202 | * [[canonicalize]] method. For example, suppose the "Resolving a Load"
|
203 | * example {@link Importer | above} returned a stylesheet that contained
|
204 | * `@use "mixins"`:
|
205 | *
|
206 | * - The compiler resolves the URL `mixins` relative to the current
|
207 | * stylesheet's canonical URL `db:foo/bar/baz/_index.scss` to get
|
208 | * `db:foo/bar/baz/mixins`.
|
209 | * - It calls [[canonicalize]] with `"db:foo/bar/baz/mixins"`.
|
210 | * - [[canonicalize]] returns `new URL("db:foo/bar/baz/_mixins.scss")`.
|
211 | *
|
212 | * Because of this, [[canonicalize]] must return a meaningful result when
|
213 | * called with a URL relative to one returned by an earlier call to
|
214 | * [[canonicalize]].
|
215 | *
|
216 | * @param url - The loaded URL. Since this might be relative, it's represented
|
217 | * as a string rather than a [[URL]] object.
|
218 | *
|
219 | * @param options.fromImport - Whether this is being invoked because of a Sass
|
220 | * `@import` rule, as opposed to a `@use` or `@forward` rule.
|
221 | *
|
222 | * This should *only* be used for determining whether or not to load
|
223 | * [import-only files](https://sass-lang.com/documentation/at-rules/import#import-only-files).
|
224 | *
|
225 | * @returns An absolute URL if this importer recognizes the `url`, or `null`
|
226 | * if it doesn't. If this returns `null`, other importers or {@link
|
227 | * Options.loadPaths | load paths} may handle the load.
|
228 | *
|
229 | * This may also return a `Promise`, but if it does the importer may only be
|
230 | * passed to [[compileAsync]] and [[compileStringAsync]], not [[compile]] or
|
231 | * [[compileString]].
|
232 | *
|
233 | * @throws any - If this importer recognizes `url` but determines that it's
|
234 | * invalid, it may throw an exception that will be wrapped by Sass. If the
|
235 | * exception object has a `message` property, it will be used as the wrapped
|
236 | * exception's message; otherwise, the exception object's `toString()` will be
|
237 | * used. This means it's safe for importers to throw plain strings.
|
238 | */
|
239 | canonicalize(
|
240 | url: string,
|
241 | options: {fromImport: boolean}
|
242 | ): PromiseOr<URL | null, sync>;
|
243 |
|
244 | /**
|
245 | * Loads the Sass text for the given `canonicalUrl`, or returns `null` if this
|
246 | * importer can't find the stylesheet it refers to.
|
247 | *
|
248 | * @param canonicalUrl - The canonical URL of the stylesheet to load. This is
|
249 | * guaranteed to come from a call to [[canonicalize]], although not every call
|
250 | * to [[canonicalize]] will result in a call to [[load]].
|
251 | *
|
252 | * @returns The contents of the stylesheet at `canonicalUrl` if it can be
|
253 | * loaded, or `null` if it can't.
|
254 | *
|
255 | * This may also return a `Promise`, but if it does the importer may only be
|
256 | * passed to [[compileAsync]] and [[compileStringAsync]], not [[compile]] or
|
257 | * [[compileString]].
|
258 | *
|
259 | * @throws any - If this importer finds a stylesheet at `url` but it fails to
|
260 | * load for some reason, or if `url` is uniquely associated with this importer
|
261 | * but doesn't refer to a real stylesheet, the importer may throw an exception
|
262 | * that will be wrapped by Sass. If the exception object has a `message`
|
263 | * property, it will be used as the wrapped exception's message; otherwise,
|
264 | * the exception object's `toString()` will be used. This means it's safe for
|
265 | * importers to throw plain strings.
|
266 | */
|
267 | load(canonicalUrl: URL): PromiseOr<ImporterResult | null, sync>;
|
268 |
|
269 | /** @hidden */
|
270 | findFileUrl?: never;
|
271 | }
|
272 |
|
273 | /**
|
274 | * The result of successfully loading a stylesheet with an [[Importer]].
|
275 | *
|
276 | * @category Importer
|
277 | */
|
278 | export interface ImporterResult {
|
279 | /** The contents of the stylesheet. */
|
280 | contents: string;
|
281 |
|
282 | /** The syntax with which to parse [[contents]]. */
|
283 | syntax: Syntax;
|
284 |
|
285 | /**
|
286 | * The URL to use to link to the loaded stylesheet's source code in source
|
287 | * maps. A `file:` URL is ideal because it's accessible to both browsers and
|
288 | * other build tools, but an `http:` URL is also acceptable.
|
289 | *
|
290 | * If this isn't set, it defaults to a `data:` URL that contains the contents
|
291 | * of the loaded stylesheet.
|
292 | */
|
293 | sourceMapUrl?: URL;
|
294 | }
|