All files / src/sass/utils getImporterList.ts

24.59% Statements 15/61
9.09% Branches 2/22
11.11% Functions 1/9
23.21% Lines 13/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 1621x                         1x 1x   1x 1x   1x 1x           1x                                                                                                                           1x       8x 8x   8x                                                                                                                               8x    
import { dirname, extname, basename } from "path";
 
import {
  LegacyOptions,
  LegacyImporter,
  Options,
  FileImporter,
  Importer,
  ImporterResult,
  CompileResult,
} from "sass";
// import resolve from 'resolve';
 
import { warn } from "./logger";
import { fileURLToPath, pathToFileURL } from "../../utils/url";
import { IsomorphicRollupFs } from "../../fs-iface";
import { createResolve, resolver } from "../../resolver";
import { getSyntax } from "./helpers";
 
const MATCH_NODE_MODULE_RE = /^~([a-z0-9]|@).+/i;
const blah: Record<string, string> = {};
 
/**
 * Returns a sass `importer` list:
 * @see https://sass-lang.com/documentation/js-api#importer
 */
export const getImporterListLegacy = (
  importOption: LegacyOptions<"async">["importer"],
  fs: IsomorphicRollupFs,
) => {
  // `Promise` to chain all `importer1` calls to;  E.g.,  subsequent `importer1` calls won't call `done` until previous `importer1` calls have called `done` (import order enforcement) - Required since importer below is actually 'async'.
  let lastResult = Promise.resolve();
  const resolve = resolver({
    fs,
    warn,
    ignoreExports: true,
    extensions: [".scss", ".sass", ".css"],
  });
 
  /**
   * Legacy Sass (*.scss/*.sass) file importer (works in new (< v2.0), and older, versions of `sass` (dart-sass) module).
   *
   * @see https://sass-lang.com/documentation/js-api/modules#LegacyAsyncImporter
   *
   * @param url - Url found in `@import`/`@use`, found in parent sass file;  E.g., exactly as it appears in sass file.
   * @param prevUrl - Url of file that contains '@import' rule for incoming file (`url`).
   * @param done - Signals import completion.  Note: `LegacyImporterResult`, and `SassImporterResult`, are the same here - We've defined the type for our plugin, since older versions of sass don't have this type defined.
   * @note This importer may not work in dart-sass v2.0+ (which may be far off in the future, but is important to note: https://sass-lang.com/documentation/js-api/#legacy-api).
   */
  const importer1: LegacyImporter<"async"> = async (url, prevUrl, done) => {
    if (!MATCH_NODE_MODULE_RE.test(url)) {
      return null;
    }
 
    const moduleUrl = url.slice(1);
    // const resolveOptions = {
    //   basedir: dirname(prevUrl),
    //   extensions: ['.scss', '.sass'],
    // };
 
    // @todo This block should run as a promise instead, will help ensure we're not blocking the thread it is
    //   running on, even though `sass` is probably already running the importer in one.
    try {
      const file = await resolve(moduleUrl, dirname(prevUrl));
      lastResult = lastResult.then(() => done({ file }));
    } catch (err) {
      warn("[rollup-plugin-sass]: Recovered from error: ", err);
      // If importer has sibling importers then exit and allow one of the other
      //  importers to attempt file path resolution.
      if (Array.isArray(importOption) && importOption.length > 1) {
        lastResult = lastResult.then(() => done(null));
        return;
      }
      lastResult = lastResult.then(() =>
        done({
          file: url,
        }),
      );
    }
  };
 
  return [importer1].concat((importOption as never) || []);
};
 
/**
 * Returns a sass `importer` list:
 * @see https://sass-lang.com/documentation/js-api/interfaces/importer/
 */
export const getImporterListModern = (
  importOption: Options<"async">["importers"],
  fs: IsomorphicRollupFs,
) => {
  const resolve = createResolve(fs);
  const resolved: string[] = [];
 
  const importer: Importer<"async"> = {
    async canonicalize(cannonicalUrl, context) {
      let path = cannonicalUrl.startsWith("sdk://")
        ? fileURLToPath(cannonicalUrl)
        : cannonicalUrl;
 
      let basedir: string;
      if (context.containingUrl) {
        basedir = dirname(fileURLToPath(context.containingUrl));
      } else {
        if (context.fromImport) {
          const causeOfPath = resolved.find((r) => path.startsWith(`${r}/`));
          if (causeOfPath) {
            basedir = causeOfPath;
            path = path.replace(`${causeOfPath}/`, "");
          } else {
            basedir = dirname(path);
          }
        } else {
          basedir = dirname(path);
        }
      }
      const resolveOptions = {
        basedir: basedir,
        extensions: [".scss", ".sass", ".css"],
      };
 
      try {
        const possibleBasenames = [basename(path), `_${basename(path)}`];
        const urlWithoutBasename = path.split("/").slice(0, -1).join("/");
        for (const base of possibleBasenames) {
          const possibleUrl = `${urlWithoutBasename}/${base}`;
          try {
            const file = await resolve(
              possibleUrl.startsWith("@") ? possibleUrl : `./${possibleUrl}`,
              resolveOptions,
            );
            if (file) {
              resolved.push(dirname(file));
              return pathToFileURL(file) as URL;
            }
          } catch (_) {}
        }
        console.error(`couldn't resolve ${path}`);
        return null;
      } catch (err) {
        console.error(
          `[rollup-plugin-sass]: error resolving import path: ${path}`,
          err,
        );
        return null;
      }
    },
    async load(url) {
      const filePath = fileURLToPath(url);
 
      return {
        contents: await fs.readFile(filePath, "utf8"),
        syntax: getSyntax(extname(url.pathname)),
        sourceMapUrl: url,
      } as ImporterResult;
    },
  };
 
  return [importer].concat((importOption as never) || []);
};