Compare commits
No commits in common. "ad38976e8650c51787eaee93e988c1b6a1d20514" and "4eb6a17e9fbd0b893cb79789a1ee107d9b320a19" have entirely different histories.
ad38976e86
...
4eb6a17e9f
21
package.json
21
package.json
|
|
@ -15,20 +15,10 @@
|
||||||
"import": "./dist/csv-loader/loader.mjs",
|
"import": "./dist/csv-loader/loader.mjs",
|
||||||
"require": "./dist/csv-loader/loader.js"
|
"require": "./dist/csv-loader/loader.js"
|
||||||
},
|
},
|
||||||
"./csv-loader/webpack": {
|
|
||||||
"types": "./dist/csv-loader/webpack.d.ts",
|
|
||||||
"import": "./dist/csv-loader/webpack.mjs",
|
|
||||||
"require": "./dist/csv-loader/webpack.js"
|
|
||||||
},
|
|
||||||
"./csv-loader/rollup": {
|
"./csv-loader/rollup": {
|
||||||
"types": "./dist/csv-loader/rollup.d.ts",
|
"types": "./dist/csv-loader/rollup.d.ts",
|
||||||
"import": "./dist/csv-loader/rollup.mjs",
|
"import": "./dist/csv-loader/rollup.mjs",
|
||||||
"require": "./dist/csv-loader/rollup.js"
|
"require": "./dist/csv-loader/rollup.js"
|
||||||
},
|
|
||||||
"./csv-loader/esbuild": {
|
|
||||||
"types": "./dist/csv-loader/esbuild.d.ts",
|
|
||||||
"import": "./dist/csv-loader/esbuild.mjs",
|
|
||||||
"require": "./dist/csv-loader/esbuild.js"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
@ -59,15 +49,6 @@
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@rspack/core": "^1.x",
|
"@rspack/core": "^1.x"
|
||||||
"esbuild": "*"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@rspack/core": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"esbuild": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
import type { CsvLoaderOptions } from './loader.js';
|
|
||||||
import { csvToModule } from './loader.js';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import type { Plugin, OnLoadResult } from 'esbuild';
|
|
||||||
|
|
||||||
export interface CsvEsbuildOptions extends CsvLoaderOptions {
|
|
||||||
/** Include pattern for CSV files (default: /\.csv$/) */
|
|
||||||
include?: RegExp | string | Array<RegExp | string>;
|
|
||||||
/** Exclude pattern for CSV files */
|
|
||||||
exclude?: RegExp | string | Array<RegExp | string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilter(
|
|
||||||
pattern: RegExp | string | Array<RegExp | string>
|
|
||||||
): RegExp {
|
|
||||||
if (pattern instanceof RegExp) return pattern;
|
|
||||||
if (Array.isArray(pattern)) {
|
|
||||||
const first = pattern[0];
|
|
||||||
return first instanceof RegExp ? first : new RegExp(first);
|
|
||||||
}
|
|
||||||
return new RegExp(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
function matchesPattern(
|
|
||||||
id: string,
|
|
||||||
pattern: RegExp | string | Array<RegExp | string> | undefined
|
|
||||||
): boolean {
|
|
||||||
if (!pattern) return true;
|
|
||||||
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
||||||
return patterns.some((p) => {
|
|
||||||
if (p instanceof RegExp) return p.test(id);
|
|
||||||
return id.includes(p);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Esbuild plugin for loading CSV files with inline-schema validation.
|
|
||||||
*/
|
|
||||||
export function csvLoader(options: CsvEsbuildOptions = {}): Plugin {
|
|
||||||
const {
|
|
||||||
include = /\.csv$/,
|
|
||||||
exclude,
|
|
||||||
emitTypes = true,
|
|
||||||
typesOutputDir = '',
|
|
||||||
writeToDisk = false,
|
|
||||||
...parseOptions
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
const includeFilter = createFilter(include);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: 'inline-schema-csv',
|
|
||||||
|
|
||||||
setup(build) {
|
|
||||||
build.onLoad({ filter: includeFilter }, async (args) => {
|
|
||||||
// Check exclude pattern
|
|
||||||
if (exclude && !matchesPattern(args.path, exclude)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the file content
|
|
||||||
let content: string;
|
|
||||||
try {
|
|
||||||
content = await fs.promises.readFile(args.path, 'utf-8');
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
errors: [{ text: `Failed to read file: ${args.path}` }],
|
|
||||||
} as OnLoadResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infer resource name from filename
|
|
||||||
const fileName = path.basename(args.path, '.csv').split('.')[0];
|
|
||||||
const resourceName = fileName
|
|
||||||
.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
|
|
||||||
.replace(/^(.)/, (_, char) => char.toUpperCase());
|
|
||||||
|
|
||||||
const result = csvToModule(content, {
|
|
||||||
...parseOptions,
|
|
||||||
emitTypes,
|
|
||||||
resourceName,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emit type definition file if enabled
|
|
||||||
if (emitTypes && result.dts) {
|
|
||||||
const dtsPath = typesOutputDir
|
|
||||||
? path.join(typesOutputDir, path.basename(args.path) + '.d.ts')
|
|
||||||
: args.path + '.d.ts';
|
|
||||||
|
|
||||||
if (writeToDisk) {
|
|
||||||
const absolutePath = path.isAbsolute(dtsPath)
|
|
||||||
? dtsPath
|
|
||||||
: path.join(process.cwd(), dtsPath);
|
|
||||||
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
|
||||||
fs.writeFileSync(absolutePath, result.dts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
contents: result.js,
|
|
||||||
loader: 'js' as const,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default csvLoader;
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
import type { LoaderContext } from '@rspack/core';
|
||||||
import { parse } from 'csv-parse/sync';
|
import { parse } from 'csv-parse/sync';
|
||||||
import { parseSchema, createValidator, parseValue } from '../index.js';
|
import { parseSchema, createValidator, parseValue } from '../index.js';
|
||||||
import type { Schema } from '../types.js';
|
import type { Schema } from '../types.js';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
export interface CsvLoaderOptions {
|
export interface CsvLoaderOptions {
|
||||||
delimiter?: string;
|
delimiter?: string;
|
||||||
|
|
@ -181,7 +184,7 @@ export function parseCsv(
|
||||||
/**
|
/**
|
||||||
* Generate JavaScript module code from CSV content.
|
* Generate JavaScript module code from CSV content.
|
||||||
* Returns a string that can be used as a module export.
|
* Returns a string that can be used as a module export.
|
||||||
*
|
*
|
||||||
* @param content - CSV content string
|
* @param content - CSV content string
|
||||||
* @param options - Parsing options
|
* @param options - Parsing options
|
||||||
* @returns JavaScript module code string
|
* @returns JavaScript module code string
|
||||||
|
|
@ -191,12 +194,59 @@ export function csvToModule(
|
||||||
options: CsvLoaderOptions & { resourceName?: string } = {}
|
options: CsvLoaderOptions & { resourceName?: string } = {}
|
||||||
): { js: string; dts?: string } {
|
): { js: string; dts?: string } {
|
||||||
const result = parseCsv(content, options);
|
const result = parseCsv(content, options);
|
||||||
|
|
||||||
const json = JSON.stringify(result.data, null, 2);
|
const json = JSON.stringify(result.data, null, 2);
|
||||||
const js = `export default ${json};`;
|
const js = `export default ${json};`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
js,
|
js,
|
||||||
dts: result.typeDefinition,
|
dts: result.typeDefinition,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function csvLoader(
|
||||||
|
this: LoaderContext<CsvLoaderOptions>,
|
||||||
|
content: string
|
||||||
|
): string | Buffer {
|
||||||
|
const options = this.getOptions() as CsvLoaderOptions | undefined;
|
||||||
|
const emitTypes = options?.emitTypes ?? true;
|
||||||
|
const typesOutputDir = options?.typesOutputDir ?? '';
|
||||||
|
const writeToDisk = options?.writeToDisk ?? false;
|
||||||
|
|
||||||
|
// Infer resource name from filename
|
||||||
|
const fileName = path.basename(this.resourcePath, '.csv').split('.')[0];
|
||||||
|
const resourceName = fileName
|
||||||
|
.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
|
||||||
|
.replace(/^(.)/, (_, char) => char.toUpperCase());
|
||||||
|
|
||||||
|
const result = parseCsv(content, { ...options, resourceName });
|
||||||
|
|
||||||
|
// Emit type definition file if enabled
|
||||||
|
if (emitTypes && result.typeDefinition) {
|
||||||
|
const context = this.context || '';
|
||||||
|
// Get relative path from context, normalize to forward slashes
|
||||||
|
let relativePath = this.resourcePath.replace(context, '');
|
||||||
|
if (relativePath.startsWith('\\') || relativePath.startsWith('/')) {
|
||||||
|
relativePath = relativePath.substring(1);
|
||||||
|
}
|
||||||
|
relativePath = relativePath.replace(/\\/g, '/');
|
||||||
|
|
||||||
|
// Replace .csv with .csv.d.ts for the output filename
|
||||||
|
const dtsFileName = `${relativePath}.d.ts`;
|
||||||
|
const outputPath = typesOutputDir
|
||||||
|
? path.join(typesOutputDir, dtsFileName)
|
||||||
|
: dtsFileName;
|
||||||
|
|
||||||
|
if (writeToDisk) {
|
||||||
|
// Write directly to disk (useful for dev server)
|
||||||
|
const absolutePath = path.join(this.context || process.cwd(), typesOutputDir || '', dtsFileName);
|
||||||
|
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
||||||
|
fs.writeFileSync(absolutePath, result.typeDefinition);
|
||||||
|
} else {
|
||||||
|
// Emit to in-memory filesystem (for production build)
|
||||||
|
this.emitFile?.(outputPath, result.typeDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `export default ${JSON.stringify(result.data, null, 2)};`;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import type { LoaderContext } from '@rspack/core';
|
|
||||||
import type { CsvLoaderOptions } from './loader.js';
|
|
||||||
import { parseCsv } from './loader.js';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
|
|
||||||
export interface CsvWebpackLoaderOptions extends CsvLoaderOptions {
|
|
||||||
/** Output directory for generated type files (relative to output path) */
|
|
||||||
typesOutputDir?: string;
|
|
||||||
/** Write .d.ts files to disk (useful for dev server) */
|
|
||||||
writeToDisk?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function csvLoader(
|
|
||||||
this: LoaderContext<CsvWebpackLoaderOptions>,
|
|
||||||
content: string
|
|
||||||
): string | Buffer {
|
|
||||||
const options = this.getOptions() as CsvWebpackLoaderOptions | undefined;
|
|
||||||
const emitTypes = options?.emitTypes ?? true;
|
|
||||||
const typesOutputDir = options?.typesOutputDir ?? '';
|
|
||||||
const writeToDisk = options?.writeToDisk ?? false;
|
|
||||||
|
|
||||||
// Infer resource name from filename
|
|
||||||
const fileName = path.basename(this.resourcePath, '.csv').split('.')[0];
|
|
||||||
const resourceName = fileName
|
|
||||||
.replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
|
|
||||||
.replace(/^(.)/, (_, char) => char.toUpperCase());
|
|
||||||
|
|
||||||
const result = parseCsv(content, { ...options, resourceName });
|
|
||||||
|
|
||||||
// Emit type definition file if enabled
|
|
||||||
if (emitTypes && result.typeDefinition) {
|
|
||||||
const context = this.context || '';
|
|
||||||
// Get relative path from context, normalize to forward slashes
|
|
||||||
let relativePath = this.resourcePath.replace(context, '');
|
|
||||||
if (relativePath.startsWith('\\') || relativePath.startsWith('/')) {
|
|
||||||
relativePath = relativePath.substring(1);
|
|
||||||
}
|
|
||||||
relativePath = relativePath.replace(/\\/g, '/');
|
|
||||||
|
|
||||||
// Replace .csv with .csv.d.ts for the output filename
|
|
||||||
const dtsFileName = `${relativePath}.d.ts`;
|
|
||||||
const outputPath = typesOutputDir
|
|
||||||
? path.join(typesOutputDir, dtsFileName)
|
|
||||||
: dtsFileName;
|
|
||||||
|
|
||||||
if (writeToDisk) {
|
|
||||||
// Write directly to disk (useful for dev server)
|
|
||||||
const absolutePath = path.join(this.context || process.cwd(), typesOutputDir || '', dtsFileName);
|
|
||||||
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
|
||||||
fs.writeFileSync(absolutePath, result.typeDefinition);
|
|
||||||
} else {
|
|
||||||
// Emit to in-memory filesystem (for production build)
|
|
||||||
this.emitFile?.(outputPath, result.typeDefinition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return `export default ${JSON.stringify(result.data, null, 2)};`;
|
|
||||||
}
|
|
||||||
|
|
@ -16,14 +16,6 @@ export default defineConfig([
|
||||||
external: ['@rspack/core', 'csv-parse'],
|
external: ['@rspack/core', 'csv-parse'],
|
||||||
clean: false,
|
clean: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
entry: ['src/csv-loader/webpack.ts'],
|
|
||||||
format: ['cjs', 'esm'],
|
|
||||||
dts: true,
|
|
||||||
outDir: 'dist/csv-loader',
|
|
||||||
external: ['@rspack/core', 'csv-parse'],
|
|
||||||
clean: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
entry: ['src/csv-loader/rollup.ts'],
|
entry: ['src/csv-loader/rollup.ts'],
|
||||||
format: ['cjs', 'esm'],
|
format: ['cjs', 'esm'],
|
||||||
|
|
@ -32,12 +24,4 @@ export default defineConfig([
|
||||||
external: ['rollup', 'csv-parse'],
|
external: ['rollup', 'csv-parse'],
|
||||||
clean: false,
|
clean: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
entry: ['src/csv-loader/esbuild.ts'],
|
|
||||||
format: ['cjs', 'esm'],
|
|
||||||
dts: true,
|
|
||||||
outDir: 'dist/csv-loader',
|
|
||||||
external: ['esbuild', 'csv-parse'],
|
|
||||||
clean: false,
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue