@equinor/fusion-imports 1.1.8 → 1.2.0-next.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/README.md +36 -1
- package/dist/esm/import-meta-resolve-plugin.js +149 -0
- package/dist/esm/import-meta-resolve-plugin.js.map +1 -0
- package/dist/esm/import-script.js +8 -0
- package/dist/esm/import-script.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/markdown-plugin.js +63 -0
- package/dist/esm/markdown-plugin.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/import-meta-resolve-plugin.d.ts +11 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/markdown-plugin.d.ts +27 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/import-meta-resolve-plugin.ts +171 -0
- package/src/import-script.ts +8 -0
- package/src/index.ts +2 -0
- package/src/markdown-plugin.ts +79 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type { BuildResult, Plugin } from 'esbuild';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Transforms import.meta.resolve() calls in code to resolved file:// URLs
|
|
7
|
+
* @param code - The code to transform
|
|
8
|
+
* @param baseDir - The base directory for resolving relative paths
|
|
9
|
+
* @returns The transformed code
|
|
10
|
+
*/
|
|
11
|
+
function transformImportMetaResolve(code: string, baseDir: string): string {
|
|
12
|
+
return code.replace(
|
|
13
|
+
/import\.meta\.resolve\((['"`])([^'"`]+)\1\)/g,
|
|
14
|
+
(match: string, quote: string, specifier: string) => {
|
|
15
|
+
try {
|
|
16
|
+
// Only resolve relative paths (./ or ../), skip package imports
|
|
17
|
+
if (!specifier.startsWith('./') && !specifier.startsWith('../')) {
|
|
18
|
+
return match;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const resolvedPath = path.resolve(baseDir, specifier);
|
|
22
|
+
const fileUrl = pathToFileURL(resolvedPath).href;
|
|
23
|
+
return `${quote}${fileUrl}${quote}`;
|
|
24
|
+
} catch {
|
|
25
|
+
// If resolution fails, leave it as-is
|
|
26
|
+
return match;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Gets the appropriate esbuild loader for a file extension
|
|
34
|
+
*/
|
|
35
|
+
function getLoader(filePath: string): 'ts' | 'tsx' | 'js' | 'jsx' | undefined {
|
|
36
|
+
const ext = path.extname(filePath);
|
|
37
|
+
if (ext === '.tsx' || ext === '.jsx') {
|
|
38
|
+
return ext === '.tsx' ? 'tsx' : 'jsx';
|
|
39
|
+
}
|
|
40
|
+
if (ext === '.ts' || ext === '.mts') {
|
|
41
|
+
return 'ts';
|
|
42
|
+
}
|
|
43
|
+
if (ext === '.js' || ext === '.mjs') {
|
|
44
|
+
return 'js';
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Safely reads a file, returning undefined on error
|
|
51
|
+
*/
|
|
52
|
+
async function readFileSafe(filePath: string): Promise<string | undefined> {
|
|
53
|
+
try {
|
|
54
|
+
const fs = await import('node:fs/promises');
|
|
55
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
56
|
+
} catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Collects output files from esbuild result (handles both write: true and write: false)
|
|
63
|
+
*/
|
|
64
|
+
async function collectOutputFiles(
|
|
65
|
+
result: BuildResult,
|
|
66
|
+
): Promise<Array<{ path: string; text: string }>> {
|
|
67
|
+
const files: Array<{ path: string; text: string }> = [];
|
|
68
|
+
const processedPaths = new Set<string>();
|
|
69
|
+
|
|
70
|
+
// Collect from outputFiles (write: false case - files in memory)
|
|
71
|
+
if (result.outputFiles) {
|
|
72
|
+
for (const outputFile of result.outputFiles) {
|
|
73
|
+
if (outputFile.path.endsWith('.js') || outputFile.path.endsWith('.mjs')) {
|
|
74
|
+
files.push({ path: outputFile.path, text: outputFile.text });
|
|
75
|
+
processedPaths.add(outputFile.path);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Collect from metafile (write: true case - files on disk)
|
|
81
|
+
if (result.metafile?.outputs) {
|
|
82
|
+
for (const outputPath of Object.keys(result.metafile.outputs)) {
|
|
83
|
+
if (processedPaths.has(outputPath)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (outputPath.endsWith('.js') || outputPath.endsWith('.mjs')) {
|
|
88
|
+
const absolutePath = path.isAbsolute(outputPath)
|
|
89
|
+
? outputPath
|
|
90
|
+
: path.resolve(process.cwd(), outputPath);
|
|
91
|
+
|
|
92
|
+
const text = (await readFileSafe(absolutePath)) ?? (await readFileSafe(outputPath));
|
|
93
|
+
if (text) {
|
|
94
|
+
files.push({ path: absolutePath, text });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return files;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates an esbuild plugin that transforms `import.meta.resolve()` calls
|
|
105
|
+
* to resolved file:// URLs at build time.
|
|
106
|
+
*
|
|
107
|
+
* This plugin is necessary because esbuild doesn't handle `import.meta.resolve()`
|
|
108
|
+
* calls during bundling - it leaves them as runtime calls. For local imports
|
|
109
|
+
* (relative paths), we need to resolve them at build time so they work correctly
|
|
110
|
+
* in the bundled output.
|
|
111
|
+
*/
|
|
112
|
+
export const importMetaResolvePlugin = (): Plugin => {
|
|
113
|
+
return {
|
|
114
|
+
name: 'import-meta-resolve',
|
|
115
|
+
setup(build) {
|
|
116
|
+
let entryPointDir: string | undefined;
|
|
117
|
+
|
|
118
|
+
// Transform source files during load phase
|
|
119
|
+
build.onLoad({ filter: /.*/ }, async (args) => {
|
|
120
|
+
// Track entry point directory (first non-node_modules file)
|
|
121
|
+
if (args.namespace === 'file' && !entryPointDir && !args.path.includes('node_modules')) {
|
|
122
|
+
entryPointDir = path.dirname(args.path);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Skip node_modules
|
|
126
|
+
if (args.path.includes('node_modules')) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Read and transform file if it contains import.meta.resolve
|
|
131
|
+
const contents = await readFileSafe(args.path);
|
|
132
|
+
if (!contents || !contents.includes('import.meta.resolve')) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const transformedContents = transformImportMetaResolve(contents, path.dirname(args.path));
|
|
137
|
+
if (transformedContents === contents) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const loader = getLoader(args.path);
|
|
142
|
+
return loader ? { contents: transformedContents, loader } : undefined;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Transform bundled output files
|
|
146
|
+
build.onEnd(async (result) => {
|
|
147
|
+
if (!entryPointDir) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const files = await collectOutputFiles(result);
|
|
152
|
+
const fs = await import('node:fs/promises');
|
|
153
|
+
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
if (!file.text.includes('import.meta.resolve')) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const transformedText = transformImportMetaResolve(file.text, entryPointDir);
|
|
160
|
+
if (transformedText !== file.text) {
|
|
161
|
+
try {
|
|
162
|
+
await fs.writeFile(file.path, transformedText, 'utf-8');
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.warn(`Failed to write transformed output to ${file.path}:`, error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
};
|
package/src/import-script.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { pathToFileURL } from 'node:url';
|
|
|
5
5
|
import { processAccessError } from './error.js';
|
|
6
6
|
|
|
7
7
|
import { readPackageUp } from 'read-package-up';
|
|
8
|
+
import { importMetaResolvePlugin } from './import-meta-resolve-plugin.js';
|
|
9
|
+
import { rawMarkdownPlugin } from './markdown-plugin.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Represents a Node.js module with an optional default export.
|
|
@@ -79,6 +81,9 @@ export const importScript = async <M extends EsmModule>(
|
|
|
79
81
|
outfile,
|
|
80
82
|
platform: 'node',
|
|
81
83
|
write: true,
|
|
84
|
+
plugins: [importMetaResolvePlugin(), rawMarkdownPlugin()],
|
|
85
|
+
// Enable metafile so the plugin can find output files when write: true
|
|
86
|
+
metafile: true,
|
|
82
87
|
},
|
|
83
88
|
options, // provided options
|
|
84
89
|
{
|
|
@@ -88,6 +93,9 @@ export const importScript = async <M extends EsmModule>(
|
|
|
88
93
|
bundle: true,
|
|
89
94
|
packages: 'external',
|
|
90
95
|
format: 'esm',
|
|
96
|
+
// Override plugins to ensure import-meta-resolve is included
|
|
97
|
+
// Ensure metafile is enabled for the plugin to work with write: true
|
|
98
|
+
metafile: options?.metafile ?? true,
|
|
91
99
|
},
|
|
92
100
|
) as BuildOptions;
|
|
93
101
|
|
package/src/index.ts
CHANGED
|
@@ -7,5 +7,7 @@ export {
|
|
|
7
7
|
type ImportConfigResult,
|
|
8
8
|
} from './import-config.js';
|
|
9
9
|
export { resolveConfigFile } from './resolve-config-file.js';
|
|
10
|
+
export { importMetaResolvePlugin as createImportMetaResolvePlugin } from './import-meta-resolve-plugin.js';
|
|
11
|
+
export { rawMarkdownPlugin } from './markdown-plugin.js';
|
|
10
12
|
|
|
11
13
|
export { FileNotFoundError, FileNotAccessibleError } from './error.js';
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Plugin } from 'esbuild';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for configuring the markdown raw plugin.
|
|
7
|
+
*/
|
|
8
|
+
export interface RawMarkdownPluginOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Regular expression filter to match file imports.
|
|
11
|
+
* @default /\.mdx?\?raw$/
|
|
12
|
+
*/
|
|
13
|
+
filter?: RegExp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates an esbuild plugin that handles `?raw` imports for markdown files.
|
|
18
|
+
*
|
|
19
|
+
* This plugin allows importing markdown files as raw strings using the `?raw` query parameter:
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import readmeContent from '../../README.md?raw';
|
|
22
|
+
* import mdxContent from '../../docs/guide.mdx?raw';
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The plugin intercepts imports ending with `?raw` (or `.md?raw`/`.mdx?raw`), reads the file content,
|
|
26
|
+
* and returns it as a default export string.
|
|
27
|
+
*
|
|
28
|
+
* @param options - Configuration options for the plugin
|
|
29
|
+
* @returns An esbuild plugin
|
|
30
|
+
*/
|
|
31
|
+
export const rawMarkdownPlugin = (options: RawMarkdownPluginOptions = {}): Plugin => {
|
|
32
|
+
const { filter = /\.mdx?\?raw$/ } = options;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
name: 'markdown-raw',
|
|
36
|
+
setup(build) {
|
|
37
|
+
// Handle imports ending with ?raw
|
|
38
|
+
build.onResolve({ filter }, (args) => {
|
|
39
|
+
// Remove the ?raw suffix to get the actual file path
|
|
40
|
+
const filePath = args.path.replace(/\?raw$/, '');
|
|
41
|
+
|
|
42
|
+
// Determine the resolve directory: use importer's directory if available, otherwise use resolveDir
|
|
43
|
+
const resolveDir = args.importer
|
|
44
|
+
? path.dirname(args.importer)
|
|
45
|
+
: args.resolveDir || process.cwd();
|
|
46
|
+
|
|
47
|
+
const resolvedPath = path.isAbsolute(filePath)
|
|
48
|
+
? filePath
|
|
49
|
+
: path.resolve(resolveDir, filePath);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
path: resolvedPath,
|
|
53
|
+
namespace: 'markdown-raw',
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Load the file content as a string
|
|
58
|
+
build.onLoad({ filter: /.*/, namespace: 'markdown-raw' }, async (args) => {
|
|
59
|
+
try {
|
|
60
|
+
const content = await readFile(args.path, 'utf-8');
|
|
61
|
+
// Export the content as a default export string
|
|
62
|
+
return {
|
|
63
|
+
contents: `export default ${JSON.stringify(content)};`,
|
|
64
|
+
loader: 'js',
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return {
|
|
68
|
+
errors: [
|
|
69
|
+
{
|
|
70
|
+
text: `Failed to read file: ${args.path}`,
|
|
71
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
};
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.2.0-next.0';
|