UNPKG

12.4 kBTypeScriptView Raw
1import {Syntax} from './options';
2import {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 */
42export 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 */
149export 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 */
278export 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}