@wyw-in-js/turbopack-loader 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.js CHANGED
@@ -6,6 +6,7 @@ import { makeCssModuleGlobal } from "./css-modules.js";
6
6
  import { writeFileIfChanged } from "./file-utils.js";
7
7
  import { insertImportStatement } from "./insert-import.js";
8
8
  const DEFAULT_EXTENSION = ".wyw-in-js.module.css";
9
+ const CSS_OUTPUT_QUERY = "__wyw_css";
9
10
  const stripQueryAndHash = (request) => {
10
11
  const queryIdx = request.indexOf("?");
11
12
  const hashIdx = request.indexOf("#");
@@ -48,7 +49,7 @@ const turbopackLoader = function turbopackLoader(content, inputSourceMap) {
48
49
  throw new Error("Async loader callback is not available");
49
50
  }
50
51
  logger("turbopack-loader %s", this.resourcePath);
51
- const { sourceMap, keepComments, prefixer, configFile, ...rest } = this.getOptions() || {};
52
+ const { sourceMap, keepComments, outputCss, cssOutputMode = "sidecar", prefixer, configFile, ...rest } = this.getOptions() || {};
52
53
  if (configFile) {
53
54
  const configPath = path.isAbsolute(configFile) ? configFile : path.join(process.cwd(), configFile);
54
55
  this.addDependency(configPath);
@@ -98,13 +99,26 @@ const turbopackLoader = function turbopackLoader(content, inputSourceMap) {
98
99
  cssText += `\n/*# sourceMappingURL=data:application/json;base64,${Buffer.from(result.cssSourceMapText).toString("base64")}*/\n`;
99
100
  }
100
101
  await Promise.all((result.dependencies ?? []).map((dep) => asyncResolve(dep, this.resourcePath)));
101
- writeFileIfChanged(cssFilePath, cssText);
102
- const importStatement = `import ${JSON.stringify(cssImportPath)};`;
102
+ if (outputCss) {
103
+ callback(null, cssText);
104
+ return;
105
+ }
106
+ let importPath = cssImportPath;
107
+ if (cssOutputMode === "query") {
108
+ importPath = `./${path.basename(this.resourcePath)}?${CSS_OUTPUT_QUERY}`;
109
+ } else {
110
+ writeFileIfChanged(cssFilePath, cssText);
111
+ }
112
+ const importStatement = `import ${JSON.stringify(importPath)};`;
103
113
  const finalCode = insertImportStatement(result.code, importStatement);
104
114
  callback(null, finalCode, result.sourceMap ?? undefined);
105
115
  return;
106
116
  }
107
- if (fs.existsSync(cssFilePath)) {
117
+ if (outputCss) {
118
+ callback(null, "");
119
+ return;
120
+ }
121
+ if (cssOutputMode !== "query" && fs.existsSync(cssFilePath)) {
108
122
  writeFileIfChanged(cssFilePath, "");
109
123
  }
110
124
  callback(null, result.code, result.sourceMap ?? undefined);
package/esm/index.js.map CHANGED
@@ -1 +1 @@
1
- {"mappings":"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","names":[],"sources":["../src/index.ts"],"version":3,"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport type { RawSourceMap } from 'source-map';\nimport type { LoaderContext, RawLoaderDefinitionFunction } from 'webpack';\n\nimport { logger } from '@wyw-in-js/shared';\nimport type { PluginOptions, Result } from '@wyw-in-js/transform';\nimport { transform, TransformCacheCollection } from '@wyw-in-js/transform';\n\nimport { makeCssModuleGlobal } from './css-modules';\nimport { writeFileIfChanged } from './file-utils';\nimport { insertImportStatement } from './insert-import';\n\nconst DEFAULT_EXTENSION = '.wyw-in-js.module.css';\n\nconst stripQueryAndHash = (request: string) => {\n const queryIdx = request.indexOf('?');\n const hashIdx = request.indexOf('#');\n\n if (queryIdx === -1) {\n return hashIdx === -1 ? request : request.slice(0, hashIdx);\n }\n if (hashIdx === -1) return request.slice(0, queryIdx);\n\n return request.slice(0, Math.min(queryIdx, hashIdx));\n};\n\nexport type LoaderOptions = {\n keepComments?: boolean;\n prefixer?: boolean;\n sourceMap?: boolean;\n} & Partial<PluginOptions>;\n\ntype Loader = RawLoaderDefinitionFunction<LoaderOptions>;\ntype ResolveFn = ReturnType<LoaderContext<LoaderOptions>['getResolve']>;\n\nconst cache = new TransformCacheCollection();\n\nfunction convertSourceMap(\n value: RawSourceMap | string | null | undefined,\n filename: string\n): RawSourceMap | undefined {\n if (typeof value === 'string' || !value) {\n return undefined;\n }\n\n return {\n ...value,\n file: value.file ?? filename,\n mappings: value.mappings ?? '',\n names: value.names ?? [],\n sources: value.sources ?? [],\n version: value.version ?? 3,\n };\n}\n\nasync function resolveWith(\n resolve: ResolveFn,\n context: string,\n request: string\n): Promise<string | false> {\n type ResolveCallback = (\n ctx: string,\n req: string,\n cb: (err: Error | null, result?: string) => void\n ) => void;\n type ResolveAsync = (ctx: string, req: string) => Promise<string | false>;\n\n if (typeof resolve !== 'function') return false;\n\n if (resolve.length >= 3) {\n return new Promise((ok, fail) => {\n (resolve as unknown as ResolveCallback)(\n context,\n request,\n (err, result) => {\n if (err) fail(err);\n else ok(result ?? false);\n }\n );\n });\n }\n\n return (resolve as unknown as ResolveAsync)(context, request);\n}\n\nconst turbopackLoader: Loader = function turbopackLoader(\n content,\n inputSourceMap\n) {\n const callbackFromAsync =\n typeof this.async === 'function' ? this.async() : undefined;\n const callback =\n typeof callbackFromAsync === 'function' ? callbackFromAsync : this.callback;\n\n if (typeof callback !== 'function') {\n throw new Error('Async loader callback is not available');\n }\n\n logger('turbopack-loader %s', this.resourcePath);\n\n const { sourceMap, keepComments, prefixer, configFile, ...rest } =\n this.getOptions() || {};\n\n if (configFile) {\n const configPath = path.isAbsolute(configFile)\n ? configFile\n : path.join(process.cwd(), configFile);\n this.addDependency(configPath);\n }\n\n const cssFileName = `${path.basename(\n this.resourcePath,\n path.extname(this.resourcePath)\n )}${DEFAULT_EXTENSION}`;\n const cssFilePath = path.join(path.dirname(this.resourcePath), cssFileName);\n const cssImportPath = `./${cssFileName}`;\n\n const resolveModule = this.getResolve({ dependencyType: 'esm' });\n\n const asyncResolve = async (token: string, importer: string) => {\n const context = path.isAbsolute(importer)\n ? path.dirname(importer)\n : path.join(process.cwd(), path.dirname(importer));\n\n const result = await resolveWith(resolveModule, context, token);\n\n if (!result) {\n throw new Error(`Cannot resolve ${token} from ${context}`);\n }\n\n const filePath = stripQueryAndHash(result);\n if (path.isAbsolute(filePath)) {\n this.addDependency(filePath);\n }\n\n return result;\n };\n\n const transformServices = {\n options: {\n filename: this.resourcePath,\n inputSourceMap: convertSourceMap(inputSourceMap, this.resourcePath),\n pluginOptions: { configFile, ...rest },\n prefixer,\n keepComments,\n root: process.cwd(),\n },\n cache,\n emitWarning: (message: string) => {\n if (typeof this.emitWarning === 'function') {\n const warning = new Error(message);\n delete warning.stack;\n this.emitWarning(warning);\n }\n },\n };\n\n transform(transformServices, content.toString(), asyncResolve)\n .then(async (result: Result) => {\n const rawCssText = result.cssText ?? '';\n\n if (rawCssText.trim()) {\n let cssText = makeCssModuleGlobal(rawCssText);\n\n if (sourceMap && typeof result.cssSourceMapText !== 'undefined') {\n cssText += `\\n/*# sourceMappingURL=data:application/json;base64,${Buffer.from(\n result.cssSourceMapText\n ).toString('base64')}*/\\n`;\n }\n\n await Promise.all(\n (result.dependencies ?? []).map((dep) =>\n asyncResolve(dep, this.resourcePath)\n )\n );\n\n writeFileIfChanged(cssFilePath, cssText);\n\n const importStatement = `import ${JSON.stringify(cssImportPath)};`;\n const finalCode = insertImportStatement(result.code, importStatement);\n\n callback(null, finalCode, result.sourceMap ?? undefined);\n return;\n }\n\n if (fs.existsSync(cssFilePath)) {\n writeFileIfChanged(cssFilePath, '');\n }\n\n callback(null, result.code, result.sourceMap ?? undefined);\n })\n .catch((err: Error) => callback(err));\n};\n\nexport default turbopackLoader;\n"],"file":"index.js"}
1
+ {"mappings":"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","names":[],"sources":["../src/index.ts"],"version":3,"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport type { RawSourceMap } from 'source-map';\nimport type { LoaderContext, RawLoaderDefinitionFunction } from 'webpack';\n\nimport { logger } from '@wyw-in-js/shared';\nimport type { PluginOptions, Result } from '@wyw-in-js/transform';\nimport { transform, TransformCacheCollection } from '@wyw-in-js/transform';\n\nimport { makeCssModuleGlobal } from './css-modules';\nimport { writeFileIfChanged } from './file-utils';\nimport { insertImportStatement } from './insert-import';\n\nconst DEFAULT_EXTENSION = '.wyw-in-js.module.css';\nconst CSS_OUTPUT_QUERY = '__wyw_css';\n\nconst stripQueryAndHash = (request: string) => {\n const queryIdx = request.indexOf('?');\n const hashIdx = request.indexOf('#');\n\n if (queryIdx === -1) {\n return hashIdx === -1 ? request : request.slice(0, hashIdx);\n }\n if (hashIdx === -1) return request.slice(0, queryIdx);\n\n return request.slice(0, Math.min(queryIdx, hashIdx));\n};\n\nexport type LoaderOptions = {\n cssOutputMode?: 'sidecar' | 'query';\n keepComments?: boolean;\n outputCss?: boolean;\n prefixer?: boolean;\n sourceMap?: boolean;\n} & Partial<PluginOptions>;\n\ntype Loader = RawLoaderDefinitionFunction<LoaderOptions>;\ntype ResolveFn = ReturnType<LoaderContext<LoaderOptions>['getResolve']>;\n\nconst cache = new TransformCacheCollection();\n\nfunction convertSourceMap(\n value: RawSourceMap | string | null | undefined,\n filename: string\n): RawSourceMap | undefined {\n if (typeof value === 'string' || !value) {\n return undefined;\n }\n\n return {\n ...value,\n file: value.file ?? filename,\n mappings: value.mappings ?? '',\n names: value.names ?? [],\n sources: value.sources ?? [],\n version: value.version ?? 3,\n };\n}\n\nasync function resolveWith(\n resolve: ResolveFn,\n context: string,\n request: string\n): Promise<string | false> {\n type ResolveCallback = (\n ctx: string,\n req: string,\n cb: (err: Error | null, result?: string) => void\n ) => void;\n type ResolveAsync = (ctx: string, req: string) => Promise<string | false>;\n\n if (typeof resolve !== 'function') return false;\n\n if (resolve.length >= 3) {\n return new Promise((ok, fail) => {\n (resolve as unknown as ResolveCallback)(\n context,\n request,\n (err, result) => {\n if (err) fail(err);\n else ok(result ?? false);\n }\n );\n });\n }\n\n return (resolve as unknown as ResolveAsync)(context, request);\n}\n\nconst turbopackLoader: Loader = function turbopackLoader(\n content,\n inputSourceMap\n) {\n const callbackFromAsync =\n typeof this.async === 'function' ? this.async() : undefined;\n const callback =\n typeof callbackFromAsync === 'function' ? callbackFromAsync : this.callback;\n\n if (typeof callback !== 'function') {\n throw new Error('Async loader callback is not available');\n }\n\n logger('turbopack-loader %s', this.resourcePath);\n\n const {\n sourceMap,\n keepComments,\n outputCss,\n cssOutputMode = 'sidecar',\n prefixer,\n configFile,\n ...rest\n } = this.getOptions() || {};\n\n if (configFile) {\n const configPath = path.isAbsolute(configFile)\n ? configFile\n : path.join(process.cwd(), configFile);\n this.addDependency(configPath);\n }\n\n const cssFileName = `${path.basename(\n this.resourcePath,\n path.extname(this.resourcePath)\n )}${DEFAULT_EXTENSION}`;\n const cssFilePath = path.join(path.dirname(this.resourcePath), cssFileName);\n const cssImportPath = `./${cssFileName}`;\n\n const resolveModule = this.getResolve({ dependencyType: 'esm' });\n\n const asyncResolve = async (token: string, importer: string) => {\n const context = path.isAbsolute(importer)\n ? path.dirname(importer)\n : path.join(process.cwd(), path.dirname(importer));\n\n const result = await resolveWith(resolveModule, context, token);\n\n if (!result) {\n throw new Error(`Cannot resolve ${token} from ${context}`);\n }\n\n const filePath = stripQueryAndHash(result);\n if (path.isAbsolute(filePath)) {\n this.addDependency(filePath);\n }\n\n return result;\n };\n\n const transformServices = {\n options: {\n filename: this.resourcePath,\n inputSourceMap: convertSourceMap(inputSourceMap, this.resourcePath),\n pluginOptions: { configFile, ...rest },\n prefixer,\n keepComments,\n root: process.cwd(),\n },\n cache,\n emitWarning: (message: string) => {\n if (typeof this.emitWarning === 'function') {\n const warning = new Error(message);\n delete warning.stack;\n this.emitWarning(warning);\n }\n },\n };\n\n transform(transformServices, content.toString(), asyncResolve)\n .then(async (result: Result) => {\n const rawCssText = result.cssText ?? '';\n\n if (rawCssText.trim()) {\n let cssText = makeCssModuleGlobal(rawCssText);\n\n if (sourceMap && typeof result.cssSourceMapText !== 'undefined') {\n cssText += `\\n/*# sourceMappingURL=data:application/json;base64,${Buffer.from(\n result.cssSourceMapText\n ).toString('base64')}*/\\n`;\n }\n\n await Promise.all(\n (result.dependencies ?? []).map((dep) =>\n asyncResolve(dep, this.resourcePath)\n )\n );\n\n if (outputCss) {\n callback(null, cssText);\n return;\n }\n\n let importPath = cssImportPath;\n if (cssOutputMode === 'query') {\n importPath = `./${path.basename(\n this.resourcePath\n )}?${CSS_OUTPUT_QUERY}`;\n } else {\n writeFileIfChanged(cssFilePath, cssText);\n }\n\n const importStatement = `import ${JSON.stringify(importPath)};`;\n const finalCode = insertImportStatement(result.code, importStatement);\n\n callback(null, finalCode, result.sourceMap ?? undefined);\n return;\n }\n\n if (outputCss) {\n callback(null, '');\n return;\n }\n\n if (cssOutputMode !== 'query' && fs.existsSync(cssFilePath)) {\n writeFileIfChanged(cssFilePath, '');\n }\n\n callback(null, result.code, result.sourceMap ?? undefined);\n })\n .catch((err: Error) => callback(err));\n};\n\nexport default turbopackLoader;\n"],"file":"index.js"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@wyw-in-js/turbopack-loader",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "dependencies": {
6
- "@wyw-in-js/shared": "2.0.0",
7
- "@wyw-in-js/transform": "2.0.2"
6
+ "@wyw-in-js/shared": "2.1.0",
7
+ "@wyw-in-js/transform": "2.1.0"
8
8
  },
9
9
  "devDependencies": {
10
10
  "@types/node": "^22.0.0",
package/types/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { RawLoaderDefinitionFunction } from 'webpack';
2
2
  import type { PluginOptions } from '@wyw-in-js/transform';
3
3
  export type LoaderOptions = {
4
+ cssOutputMode?: 'sidecar' | 'query';
4
5
  keepComments?: boolean;
6
+ outputCss?: boolean;
5
7
  prefixer?: boolean;
6
8
  sourceMap?: boolean;
7
9
  } & Partial<PluginOptions>;
package/types/index.js CHANGED
@@ -6,6 +6,7 @@ import { makeCssModuleGlobal } from './css-modules';
6
6
  import { writeFileIfChanged } from './file-utils';
7
7
  import { insertImportStatement } from './insert-import';
8
8
  const DEFAULT_EXTENSION = '.wyw-in-js.module.css';
9
+ const CSS_OUTPUT_QUERY = '__wyw_css';
9
10
  const stripQueryAndHash = (request) => {
10
11
  const queryIdx = request.indexOf('?');
11
12
  const hashIdx = request.indexOf('#');
@@ -52,7 +53,7 @@ const turbopackLoader = function turbopackLoader(content, inputSourceMap) {
52
53
  throw new Error('Async loader callback is not available');
53
54
  }
54
55
  logger('turbopack-loader %s', this.resourcePath);
55
- const { sourceMap, keepComments, prefixer, configFile, ...rest } = this.getOptions() || {};
56
+ const { sourceMap, keepComments, outputCss, cssOutputMode = 'sidecar', prefixer, configFile, ...rest } = this.getOptions() || {};
56
57
  if (configFile) {
57
58
  const configPath = path.isAbsolute(configFile)
58
59
  ? configFile
@@ -104,13 +105,27 @@ const turbopackLoader = function turbopackLoader(content, inputSourceMap) {
104
105
  cssText += `\n/*# sourceMappingURL=data:application/json;base64,${Buffer.from(result.cssSourceMapText).toString('base64')}*/\n`;
105
106
  }
106
107
  await Promise.all((result.dependencies ?? []).map((dep) => asyncResolve(dep, this.resourcePath)));
107
- writeFileIfChanged(cssFilePath, cssText);
108
- const importStatement = `import ${JSON.stringify(cssImportPath)};`;
108
+ if (outputCss) {
109
+ callback(null, cssText);
110
+ return;
111
+ }
112
+ let importPath = cssImportPath;
113
+ if (cssOutputMode === 'query') {
114
+ importPath = `./${path.basename(this.resourcePath)}?${CSS_OUTPUT_QUERY}`;
115
+ }
116
+ else {
117
+ writeFileIfChanged(cssFilePath, cssText);
118
+ }
119
+ const importStatement = `import ${JSON.stringify(importPath)};`;
109
120
  const finalCode = insertImportStatement(result.code, importStatement);
110
121
  callback(null, finalCode, result.sourceMap ?? undefined);
111
122
  return;
112
123
  }
113
- if (fs.existsSync(cssFilePath)) {
124
+ if (outputCss) {
125
+ callback(null, '');
126
+ return;
127
+ }
128
+ if (cssOutputMode !== 'query' && fs.existsSync(cssFilePath)) {
114
129
  writeFileIfChanged(cssFilePath, '');
115
130
  }
116
131
  callback(null, result.code, result.sourceMap ?? undefined);