UNPKG

ng-packagr

Version:

Compile and package Angular libraries in Angular Package Format (APF)

197 lines 9.47 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = renderSassStylesheet; const remapping_1 = __importDefault(require("@ampproject/remapping")); const node_path_1 = require("node:path"); const node_url_1 = require("node:url"); const node_worker_threads_1 = require("node:worker_threads"); const sass_1 = require("sass"); const rebasing_importer_1 = require("./rebasing-importer"); async function renderSassStylesheet(request) { var _a, _b; const { importerChannel, hasLogger, source, options, rebase } = request; const entryDirectory = (0, node_path_1.dirname)(options.url); let warnings; try { const directoryCache = new Map(); const rebaseSourceMaps = options.sourceMap ? new Map() : undefined; if (importerChannel) { // When a custom importer function is present, the importer request must be proxied // back to the main thread where it can be executed. // This process must be synchronous from the perspective of dart-sass. The `Atomics` // functions combined with the shared memory `importSignal` and the Node.js // `receiveMessageOnPort` function are used to ensure synchronous behavior. const proxyImporter = { findFileUrl: (url, { fromImport, containingUrl }) => { var _a; Atomics.store(importerChannel.signal, 0, 0); importerChannel.port.postMessage({ url, options: { fromImport, containingUrl: containingUrl ? (0, node_url_1.fileURLToPath)(containingUrl) : null, }, }); // Wait for the main thread to set the signal to 1 and notify, which tells // us that a message can be received on the port. // If the main thread is fast, the signal will already be set to 1, and no // sleep/notify is necessary. // However, there can be a race condition here: // - the main thread sets the signal to 1, but does not get to the notify instruction yet // - the worker does not pause because the signal is set to 1 // - the worker very soon enters this method again // - this method sets the signal to 0 and sends the message // - the signal is 0 and so the `Atomics.wait` call blocks // - only now the main thread runs the `notify` from the first invocation, so the // worker continues. // - but there is no message yet in the port, because the thread should not have been // waken up yet. // To combat this, wait for a non-0 value _twice_. // Almost every time, this immediately continues with "not-equal", because // the signal is still set to 1, except during the race condition, when the second // wait will wait for the correct notify. Atomics.wait(importerChannel.signal, 0, 0); Atomics.wait(importerChannel.signal, 0, 0); const result = (_a = (0, node_worker_threads_1.receiveMessageOnPort)(importerChannel.port)) === null || _a === void 0 ? void 0 : _a.message; return result ? (0, node_url_1.pathToFileURL)(result) : null; }, }; options.importers = [ rebase ? (0, rebasing_importer_1.sassBindWorkaround)(new rebasing_importer_1.ModuleUrlRebasingImporter(entryDirectory, directoryCache, proxyImporter.findFileUrl)) : proxyImporter, ]; } if (rebase && ((_a = options.loadPaths) === null || _a === void 0 ? void 0 : _a.length)) { (_b = options.importers) !== null && _b !== void 0 ? _b : (options.importers = []); options.importers.push((0, rebasing_importer_1.sassBindWorkaround)(new rebasing_importer_1.LoadPathsUrlRebasingImporter(entryDirectory, directoryCache, options.loadPaths))); options.loadPaths = undefined; } let relativeImporter; if (rebase) { relativeImporter = (0, rebasing_importer_1.sassBindWorkaround)(new rebasing_importer_1.RelativeUrlRebasingImporter(entryDirectory, directoryCache)); } // The synchronous Sass render function can be up to two times faster than the async variant const result = (0, sass_1.compileString)(source, { ...options, // URL is not serializable so to convert to string in the parent and back to URL here. url: (0, node_url_1.pathToFileURL)(options.url), // The `importer` option (singular) handles relative imports importer: relativeImporter, logger: hasLogger ? { warn(message, warnOptions) { warnings !== null && warnings !== void 0 ? warnings : (warnings = []); warnings.push({ ...warnOptions, message, span: warnOptions.span && convertSourceSpan(warnOptions.span), ...convertDeprecation(warnOptions.deprecation, // eslint-disable-next-line @typescript-eslint/no-explicit-any warnOptions.deprecationType), }); }, } : undefined, }); if (result.sourceMap && (rebaseSourceMaps === null || rebaseSourceMaps === void 0 ? void 0 : rebaseSourceMaps.size)) { // Merge the intermediate rebasing source maps into the final Sass generated source map. // Casting is required due to small but compatible differences in typings between the packages. result.sourceMap = (0, remapping_1.default)(result.sourceMap, // To prevent an infinite lookup loop, skip getting the source when the rebasing source map // is referencing its original self. (file, context) => (file !== context.importer ? rebaseSourceMaps.get(file) : null)); } return { warnings, result: { ...result, sourceMap: result.sourceMap, // URL is not serializable so to convert to string here and back to URL in the parent. loadedUrls: result.loadedUrls.map(p => (0, node_url_1.fileURLToPath)(p)), }, }; } catch (error) { // Needed because V8 will only serialize the message and stack properties of an Error instance. if (error instanceof sass_1.Exception) { const { span, message, stack, sassMessage, sassStack } = error; return { warnings, error: { span: convertSourceSpan(span), message, stack, sassMessage, sassStack, }, }; } else if (error instanceof Error) { const { message, stack } = error; return { warnings, error: { message, stack } }; } else { return { warnings, error: { message: 'An unknown error has occurred.' }, }; } } } /** * Converts a Sass SourceSpan object into a serializable form. * The SourceSpan object contains a URL property which must be converted into a string. * Also, most of the interface's properties are get accessors and are not automatically * serialized when sent back from the worker. * * @param span The Sass SourceSpan object to convert. * @returns A serializable form of the SourceSpan object. */ function convertSourceSpan(span) { return { text: span.text, context: span.context, end: { column: span.end.column, offset: span.end.offset, line: span.end.line, }, start: { column: span.start.column, offset: span.start.offset, line: span.start.line, }, url: span.url ? (0, node_url_1.fileURLToPath)(span.url) : undefined, }; } function convertDeprecation(deprecation, deprecationType) { if (!deprecation || !deprecationType) { return { deprecation: false }; } const { obsoleteIn, deprecatedIn, ...rest } = deprecationType; return { deprecation: true, deprecationType: { ...rest, obsoleteIn: obsoleteIn ? { major: obsoleteIn.major, minor: obsoleteIn.minor, patch: obsoleteIn.patch, } : null, deprecatedIn: deprecatedIn ? { major: deprecatedIn.major, minor: deprecatedIn.minor, patch: deprecatedIn.patch, } : null, }, }; } //# sourceMappingURL=worker.js.map