UNPKG

saepequia

Version:

A simple, maximally extensible, dependency minimized framework for building modern Ethereum dApps

153 lines (128 loc) 4.5 kB
/** * This file contains a Webpack loader for Linaria. * It uses the transform.ts function to generate class names from source code, * returns transformed code without template literals and attaches generated source maps */ import fs from 'fs'; import path from 'path'; import mkdirp from 'mkdirp'; import normalize from 'normalize-path'; import loaderUtils from 'loader-utils'; import enhancedResolve from 'enhanced-resolve'; import findYarnWorkspaceRoot from 'find-yarn-workspace-root'; import type { RawSourceMap } from 'source-map'; import cosmiconfig from 'cosmiconfig'; import * as EvalCache from './babel/eval-cache'; import Module from './babel/module'; import { debug } from './babel/utils/logger'; import transform from './transform'; const workspaceRoot = findYarnWorkspaceRoot(); const lernaConfig = cosmiconfig('lerna', { searchPlaces: ['lerna.json'], }).searchSync(); const lernaRoot = lernaConfig !== null ? path.dirname(lernaConfig.filepath) : null; type LoaderContext = Parameters<typeof loaderUtils.getOptions>[0]; export default function loader( this: LoaderContext, content: string, inputSourceMap: RawSourceMap | null ) { debug('loader', this.resourcePath); EvalCache.clearForFile(this.resourcePath); const { sourceMap = undefined, cacheDirectory = '.linaria-cache', preprocessor = undefined, extension = '.linaria.css', ...rest } = loaderUtils.getOptions(this) || {}; const root = workspaceRoot || lernaRoot || process.cwd(); const baseOutputFileName = this.resourcePath.replace(/\.[^.]+$/, extension); const outputFilename = normalize( path.join( path.isAbsolute(cacheDirectory) ? cacheDirectory : path.join(process.cwd(), cacheDirectory), this.resourcePath.includes(root) ? path.relative(root, baseOutputFileName) : baseOutputFileName ) ); const resolveOptions = { extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], }; const resolveSync = enhancedResolve.create.sync( // this._compilation is a deprecated API // However there seems to be no other way to access webpack's resolver // There is this.resolve, but it's asynchronous // Another option is to read the webpack.config.js, but it won't work for programmatic usage // This API is used by many loaders/plugins, so hope we're safe for a while this._compilation?.options.resolve ? { ...resolveOptions, alias: this._compilation.options.resolve.alias, modules: this._compilation.options.resolve.modules, } : resolveOptions ); let result; const originalResolveFilename = Module._resolveFilename; try { // Use webpack's resolution when evaluating modules Module._resolveFilename = (id, { filename }) => resolveSync(path.dirname(filename), id); result = transform(content, { filename: path.relative(process.cwd(), this.resourcePath), inputSourceMap: inputSourceMap ?? undefined, outputFilename, pluginOptions: rest, preprocessor, }); } finally { // Restore original behaviour Module._resolveFilename = originalResolveFilename; } if (result.cssText) { let { cssText } = result; if (sourceMap) { cssText += `/*# sourceMappingURL=data:application/json;base64,${Buffer.from( result.cssSourceMapText || '' ).toString('base64')}*/`; } if (result.dependencies?.length) { result.dependencies.forEach((dep) => { try { const f = resolveSync(path.dirname(this.resourcePath), dep); this.addDependency(f); } catch (e) { // eslint-disable-next-line no-console console.warn(`[linaria] failed to add dependency for: ${dep}`, e); } }); } // Read the file first to compare the content // Write the new content only if it's changed // This will prevent unnecessary WDS reloads let currentCssText; try { currentCssText = fs.readFileSync(outputFilename, 'utf-8'); } catch (e) { // Ignore error } if (currentCssText !== cssText) { mkdirp.sync(path.dirname(outputFilename)); fs.writeFileSync(outputFilename, cssText); } this.callback( null, `${result.code}\n\nrequire(${loaderUtils.stringifyRequest( this, outputFilename )});`, result.sourceMap ?? undefined ); return; } this.callback(null, result.code, result.sourceMap ?? undefined); }