You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.esm.js 5.6 KiB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import {
  2. ensureDir,
  3. readFile,
  4. readdirSync,
  5. statSync,
  6. writeFile
  7. } from "fs-extra";
  8. import {TextDecoder} from "util";
  9. import path from "path";
  10. import tmp from "tmp";
  11. import postcss from "postcss";
  12. import postcssModules from "postcss-modules";
  13. import less from "less";
  14. import stylus from "stylus";
  15. import resolveFile from "resolve-file";
  16. const defaultOptions = {
  17. plugins: [],
  18. modules: true,
  19. rootDir: process.cwd(),
  20. sassOptions: {},
  21. lessOptions: {},
  22. stylusOptions: {},
  23. fileIsModule: null
  24. };
  25. const postCSSPlugin = ({
  26. plugins = [],
  27. modules = true,
  28. rootDir = process.cwd(),
  29. sassOptions = {},
  30. lessOptions = {},
  31. stylusOptions = {},
  32. fileIsModule
  33. } = defaultOptions) => ({
  34. name: "postcss2",
  35. setup(build) {
  36. const tmpDirPath = tmp.dirSync().name, modulesMap = [];
  37. const modulesPlugin = postcssModules({
  38. generateScopedName: "[name]__[local]___[hash:base64:5]",
  39. ...typeof modules !== "boolean" ? modules : {},
  40. getJSON(filepath, json, outpath) {
  41. const mapIndex = modulesMap.findIndex((m) => m.path === filepath);
  42. if (mapIndex !== -1) {
  43. modulesMap[mapIndex].map = json;
  44. } else {
  45. modulesMap.push({
  46. path: filepath,
  47. map: json
  48. });
  49. }
  50. if (typeof modules !== "boolean" && typeof modules.getJSON === "function")
  51. return modules.getJSON(filepath, json, outpath);
  52. }
  53. });
  54. build.onResolve({filter: /.\.(css|sass|scss|less|styl)$/}, async (args) => {
  55. if (args.namespace !== "file" && args.namespace !== "")
  56. return;
  57. let sourceFullPath = resolveFile(args.path);
  58. if (!sourceFullPath)
  59. sourceFullPath = path.resolve(args.resolveDir, args.path);
  60. const sourceExt = path.extname(sourceFullPath);
  61. const sourceBaseName = path.basename(sourceFullPath, sourceExt);
  62. const isModule = fileIsModule ? fileIsModule(sourceFullPath) : sourceBaseName.match(/\.module$/);
  63. const sourceDir = path.dirname(sourceFullPath);
  64. const watchFiles = [sourceFullPath];
  65. let tmpFilePath;
  66. if (args.kind === "entry-point") {
  67. const sourceRelDir = path.relative(path.dirname(rootDir), path.dirname(sourceFullPath));
  68. tmpFilePath = path.resolve(tmpDirPath, sourceRelDir, `${sourceBaseName}.css`);
  69. await ensureDir(path.dirname(tmpFilePath));
  70. } else {
  71. const uniqueTmpDir = path.resolve(tmpDirPath, uniqueId());
  72. tmpFilePath = path.resolve(uniqueTmpDir, `${sourceBaseName}.css`);
  73. }
  74. await ensureDir(path.dirname(tmpFilePath));
  75. const fileContent = await readFile(sourceFullPath);
  76. let css = sourceExt === ".css" ? fileContent : "";
  77. if (sourceExt === ".sass" || sourceExt === ".scss") {
  78. const sassResult = await renderSass({
  79. ...sassOptions,
  80. file: sourceFullPath
  81. });
  82. css = sassResult.css.toString();
  83. watchFiles.push(...sassResult.stats.includedFiles);
  84. }
  85. if (sourceExt === ".styl")
  86. css = await renderStylus(new TextDecoder().decode(fileContent), {
  87. ...stylusOptions,
  88. filename: sourceFullPath
  89. });
  90. if (sourceExt === ".less")
  91. css = (await less.render(new TextDecoder().decode(fileContent), {
  92. ...lessOptions,
  93. filename: sourceFullPath,
  94. rootpath: path.dirname(args.path)
  95. })).css;
  96. const result = await postcss(isModule ? [modulesPlugin, ...plugins] : plugins).process(css, {
  97. from: sourceFullPath,
  98. to: tmpFilePath
  99. });
  100. watchFiles.push(...getPostCssDependencies(result.messages));
  101. await writeFile(tmpFilePath, result.css);
  102. return {
  103. namespace: isModule ? "postcss-module" : "file",
  104. path: tmpFilePath,
  105. watchFiles,
  106. pluginData: {
  107. originalPath: sourceFullPath
  108. }
  109. };
  110. });
  111. build.onLoad({filter: /.*/, namespace: "postcss-module"}, async (args) => {
  112. const mod = modulesMap.find(({path: path2}) => path2 === args?.pluginData?.originalPath), resolveDir = path.dirname(args.path);
  113. return {
  114. resolveDir,
  115. contents: `import ${JSON.stringify(args.path)};
  116. export default ${JSON.stringify(mod && mod.map ? mod.map : {})};`
  117. };
  118. });
  119. }
  120. });
  121. function renderSass(options) {
  122. return new Promise((resolve, reject) => {
  123. getSassImpl().render(options, (e, res) => {
  124. if (e)
  125. reject(e);
  126. else
  127. resolve(res);
  128. });
  129. });
  130. }
  131. function renderStylus(str, options) {
  132. return new Promise((resolve, reject) => {
  133. stylus.render(str, options, (e, res) => {
  134. if (e)
  135. reject(e);
  136. else
  137. resolve(res);
  138. });
  139. });
  140. }
  141. function getSassImpl() {
  142. let impl = "sass";
  143. try {
  144. require.resolve("sass");
  145. } catch {
  146. try {
  147. require.resolve("node-sass");
  148. impl = "node-sass";
  149. } catch {
  150. throw new Error('Please install "sass" or "node-sass" package');
  151. }
  152. }
  153. return require(impl);
  154. }
  155. function getFilesRecursive(directory) {
  156. return readdirSync(directory).reduce((files, file) => {
  157. const name = path.join(directory, file);
  158. return statSync(name).isDirectory() ? [...files, ...getFilesRecursive(name)] : [...files, name];
  159. }, []);
  160. }
  161. let idCounter = 0;
  162. function uniqueId() {
  163. return Date.now().toString(16) + (idCounter++).toString(16);
  164. }
  165. function getPostCssDependencies(messages) {
  166. let dependencies = [];
  167. for (const message of messages) {
  168. if (message.type == "dir-dependency") {
  169. dependencies.push(...getFilesRecursive(message.dir));
  170. } else if (message.type == "dependency") {
  171. dependencies.push(message.file);
  172. }
  173. }
  174. return dependencies;
  175. }
  176. var src_default = postCSSPlugin;
  177. export {
  178. src_default as default,
  179. defaultOptions
  180. };