eyeglass
Version:
Sass modules for npm.
97 lines (88 loc) • 3.59 kB
text/typescript
import { existsSync } from "fs";
import type { AsyncImporter } from "node-sass";
import EyeglassModule from "../modules/EyeglassModule";
import { URI } from "../util/URI";
import { ImporterFactory } from "./ImporterFactory";
import ImportUtilities from "./ImportUtilities";
import { ROOT_NAME } from "../modules/EyeglassModules";
import { isPresent } from "../util/typescriptUtils";
interface HasAssets {
name?: string;
assets?: {
asAssetImport: (name: string | undefined) => string;
cacheKey: (name: string) => string;
};
}
// import pattern matches `assets` and `foo/assets`, but not `foo/bar/assets`
const rAssetsImport = /^(?:([^/]+)\/)?assets$/;
const AssetImporter: ImporterFactory = function (eyeglass, sass, options, fallbackImporter?: AsyncImporter | Array<AsyncImporter>): AsyncImporter {
return ImportUtilities.createImporter("assets", function(uri, prev, done) {
let importUtils = new ImportUtilities(eyeglass, sass, options, fallbackImporter, this);
let isRealFile = existsSync(prev);
let mod: EyeglassModule | null;
function importAssetsFor(mod: HasAssets): void {
let file = "autoGenerated:" + URI.join(mod.name || ROOT_NAME, "assets");
let contents: () => string;
function getAssetImport(): string {
// XXX what is the correct behavior for when mod.name isn't found?
return mod.assets ? mod.assets.asAssetImport(mod.name) : "";
}
// allow build tools to specify a function to cache the imports
if (isPresent(mod.assets) && isPresent(options.assetsCache)) {
contents = options.assetsCache.bind(options.assetsCache,
mod.assets.cacheKey(mod.name || ROOT_NAME), getAssetImport);
} else {
contents = getAssetImport;
}
importUtils.importOnce({file, contents}, done);
}
let isRelativeImport = URI.isRelative(uri);
let assetsImport = !isRelativeImport && rAssetsImport.exec(uri);
// the first fragment of the match is the module name
let moduleName = assetsImport && assetsImport[1];
// if it's not an assets import, or it's `eyeglass/assets`,
// just use the fallback importer
if (!assetsImport) {
importUtils.fallback(uri, prev, done, function() {
done(null);
});
} else {
// if the module name wasn't specified in the import,
// infer it from the origin
if (!moduleName && isRealFile) {
mod = eyeglass.modules.findByPath(prev);
// if it's an eyeglass module...
if (mod && mod.isEyeglassModule) {
// use the module's name
moduleName = mod.name;
}
}
// if we have a module name...
if (moduleName) {
// try to access the module
mod = eyeglass.modules.access(moduleName, isRealFile ? prev : options.eyeglass.root);
// if we can access it...
if (mod) {
/* eslint max-depth:0 */
// if it has assets...
if (mod.assets) {
// import them
importAssetsFor(mod);
} else {
// otherwise throw an error
done(new Error("No assets specified for eyeglass plugin " + mod.name));
}
} else {
// if we can't find/access it, throw an error
done(new Error("No eyeglass plugin named: " + moduleName));
}
} else {
// otherwise, import the app assets
// TODO: Move app assets to the root module and pass the root eyeglass
// module here
importAssetsFor(eyeglass);
}
}
});
};
export default AssetImporter;