@vigla/vite-plugin 0.0.0-stage → 0.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/dist/api.d.ts +12 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +1 -0
- package/dist/api.js.map +1 -0
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +48 -0
- package/dist/compile.js.map +1 -0
- package/dist/compiler.d.ts +115 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +1 -0
- package/dist/compiler.js.map +1 -0
- package/dist/config-file.d.ts +10 -0
- package/dist/config-file.d.ts.map +1 -0
- package/dist/config-file.js +63 -0
- package/dist/config-file.js.map +1 -0
- package/dist/dependencies.d.ts +3 -0
- package/dist/dependencies.d.ts.map +1 -0
- package/dist/dependencies.js +40 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/error.d.ts +7 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +49 -0
- package/dist/error.js.map +1 -0
- package/dist/front-end.d.ts +24 -0
- package/dist/front-end.d.ts.map +1 -0
- package/dist/front-end.js +120 -0
- package/dist/front-end.js.map +1 -0
- package/dist/id.d.ts +40 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +144 -0
- package/dist/id.js.map +1 -0
- package/dist/identity.d.ts +41 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +31 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +17 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +92 -0
- package/dist/log.js.map +1 -0
- package/dist/maps.d.ts +8 -0
- package/dist/maps.d.ts.map +1 -0
- package/dist/maps.js +37 -0
- package/dist/maps.js.map +1 -0
- package/dist/messages.d.ts +17 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +16 -0
- package/dist/messages.js.map +1 -0
- package/dist/options.d.ts +26 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +187 -0
- package/dist/options.js.map +1 -0
- package/dist/plugins/compile-module.d.ts +5 -0
- package/dist/plugins/compile-module.d.ts.map +1 -0
- package/dist/plugins/compile-module.js +50 -0
- package/dist/plugins/compile-module.js.map +1 -0
- package/dist/plugins/compile.d.ts +5 -0
- package/dist/plugins/compile.d.ts.map +1 -0
- package/dist/plugins/compile.js +40 -0
- package/dist/plugins/compile.js.map +1 -0
- package/dist/plugins/configure.d.ts +6 -0
- package/dist/plugins/configure.d.ts.map +1 -0
- package/dist/plugins/configure.js +54 -0
- package/dist/plugins/configure.js.map +1 -0
- package/dist/plugins/hot-update.d.ts +5 -0
- package/dist/plugins/hot-update.d.ts.map +1 -0
- package/dist/plugins/hot-update.js +137 -0
- package/dist/plugins/hot-update.js.map +1 -0
- package/dist/plugins/load.d.ts +6 -0
- package/dist/plugins/load.d.ts.map +1 -0
- package/dist/plugins/load.js +77 -0
- package/dist/plugins/load.js.map +1 -0
- package/dist/plugins/optimizer.d.ts +5 -0
- package/dist/plugins/optimizer.d.ts.map +1 -0
- package/dist/plugins/optimizer.js +174 -0
- package/dist/plugins/optimizer.js.map +1 -0
- package/dist/plugins/preprocess.d.ts +11 -0
- package/dist/plugins/preprocess.d.ts.map +1 -0
- package/dist/plugins/preprocess.js +142 -0
- package/dist/plugins/preprocess.js.map +1 -0
- package/dist/plugins/runtime-scan.d.ts +4 -0
- package/dist/plugins/runtime-scan.d.ts.map +1 -0
- package/dist/plugins/runtime-scan.js +56 -0
- package/dist/plugins/runtime-scan.js.map +1 -0
- package/dist/plugins/scan.d.ts +5 -0
- package/dist/plugins/scan.d.ts.map +1 -0
- package/dist/plugins/scan.js +198 -0
- package/dist/plugins/scan.js.map +1 -0
- package/dist/preprocess.d.ts +6 -0
- package/dist/preprocess.d.ts.map +1 -0
- package/dist/preprocess.js +164 -0
- package/dist/preprocess.js.map +1 -0
- package/dist/public.d.ts +47 -0
- package/dist/public.d.ts.map +1 -0
- package/dist/public.js +1 -0
- package/dist/public.js.map +1 -0
- package/dist/vite-config.d.ts +14 -0
- package/dist/vite-config.d.ts.map +1 -0
- package/dist/vite-config.js +167 -0
- package/dist/vite-config.js.map +1 -0
- package/dist/warnings.d.ts +6 -0
- package/dist/warnings.d.ts.map +1 -0
- package/dist/warnings.js +57 -0
- package/dist/warnings.js.map +1 -0
- package/package.json +24 -5
- package/src/api.ts +12 -0
- package/src/compile.ts +96 -0
- package/src/compiler.ts +129 -0
- package/src/config-file.ts +75 -0
- package/src/dependencies.ts +47 -0
- package/src/error.ts +64 -0
- package/src/front-end.ts +146 -0
- package/src/id.ts +225 -0
- package/src/identity.ts +74 -0
- package/src/index.ts +61 -0
- package/src/log.ts +105 -0
- package/src/maps.ts +36 -0
- package/src/messages.ts +40 -0
- package/src/options.ts +257 -0
- package/src/plugins/compile-module.ts +65 -0
- package/src/plugins/compile.ts +49 -0
- package/src/plugins/configure.ts +63 -0
- package/src/plugins/hot-update.ts +159 -0
- package/src/plugins/load.ts +89 -0
- package/src/plugins/optimizer.ts +262 -0
- package/src/plugins/preprocess.ts +163 -0
- package/src/plugins/runtime-scan.ts +75 -0
- package/src/plugins/scan.ts +206 -0
- package/src/preprocess.ts +190 -0
- package/src/public.ts +56 -0
- package/src/vite-config.ts +252 -0
- package/src/warnings.ts +66 -0
- package/README.md +0 -3
package/src/compile.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { basename, combine } from 'vigla/compiler';
|
|
2
|
+
import type { Environment } from 'vite';
|
|
3
|
+
import type { CompileError, CompileOptions, CompileResult } from './compiler.ts';
|
|
4
|
+
import { enhanceCompileError } from './error.ts';
|
|
5
|
+
import type { ComponentRequest } from './id.ts';
|
|
6
|
+
import type { Identity } from './identity.ts';
|
|
7
|
+
import type { Log } from './log.ts';
|
|
8
|
+
import { mapToRelative } from './maps.ts';
|
|
9
|
+
import type { ResolvedOptions } from './options.ts';
|
|
10
|
+
|
|
11
|
+
export interface Compiled {
|
|
12
|
+
filename: string;
|
|
13
|
+
normalizedFilename: string;
|
|
14
|
+
cssId: string;
|
|
15
|
+
compiled: CompileResult;
|
|
16
|
+
ssr: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function dynamicOptions(
|
|
20
|
+
options: ResolvedOptions,
|
|
21
|
+
filename: string,
|
|
22
|
+
code: string,
|
|
23
|
+
compileOptions: CompileOptions,
|
|
24
|
+
environment: Environment,
|
|
25
|
+
log: Log,
|
|
26
|
+
scope: string
|
|
27
|
+
): Promise<Partial<CompileOptions> | undefined> {
|
|
28
|
+
const dynamic = await options.dynamicCompileOptions?.({
|
|
29
|
+
filename,
|
|
30
|
+
code,
|
|
31
|
+
compileOptions,
|
|
32
|
+
environment
|
|
33
|
+
});
|
|
34
|
+
if (dynamic && log.debug.enabled) {
|
|
35
|
+
log.debug(
|
|
36
|
+
`dynamic compile options for ${filename}: ${JSON.stringify(dynamic)}`,
|
|
37
|
+
undefined,
|
|
38
|
+
scope
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return dynamic || undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function compileComponent(
|
|
45
|
+
request: ComponentRequest,
|
|
46
|
+
code: string,
|
|
47
|
+
options: ResolvedOptions,
|
|
48
|
+
environment: Environment,
|
|
49
|
+
{ log, names }: Identity,
|
|
50
|
+
sourcemap?: object
|
|
51
|
+
): Promise<Compiled> {
|
|
52
|
+
const { filename, normalizedFilename, cssId, ssr, raw, frontEnd } = request;
|
|
53
|
+
const compileOptions: CompileOptions = {
|
|
54
|
+
...options.compilerOptions,
|
|
55
|
+
filename,
|
|
56
|
+
generate: ssr ? 'server' : 'client'
|
|
57
|
+
};
|
|
58
|
+
const dynamic = await dynamicOptions(
|
|
59
|
+
options,
|
|
60
|
+
filename,
|
|
61
|
+
code,
|
|
62
|
+
compileOptions,
|
|
63
|
+
environment,
|
|
64
|
+
log,
|
|
65
|
+
'compile'
|
|
66
|
+
);
|
|
67
|
+
const final: CompileOptions = dynamic ? { ...compileOptions, ...dynamic } : compileOptions;
|
|
68
|
+
delete final.sourcemap;
|
|
69
|
+
let compiled: CompileResult;
|
|
70
|
+
try {
|
|
71
|
+
compiled = frontEnd.compiler.compile(code, { ...final, filename });
|
|
72
|
+
if (
|
|
73
|
+
options.server?.config.experimental.hmrPartialAccept &&
|
|
74
|
+
compiled.js.code.includes('import.meta.hot.accept(')
|
|
75
|
+
) {
|
|
76
|
+
compiled.js.code = compiled.js.code.replaceAll(
|
|
77
|
+
'import.meta.hot.accept(',
|
|
78
|
+
'import.meta.hot.acceptExports(["default"],'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
enhanceCompileError(error as CompileError, code, options.preprocess, names.links);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
if (compiled.css?.map && sourcemap) {
|
|
86
|
+
compiled.css.map = combine(basename(filename), [compiled.css.map, sourcemap] as Parameters<
|
|
87
|
+
typeof combine
|
|
88
|
+
>[1]) as typeof compiled.css.map;
|
|
89
|
+
}
|
|
90
|
+
mapToRelative(compiled.js?.map, filename);
|
|
91
|
+
mapToRelative(compiled.css?.map, filename);
|
|
92
|
+
if (!raw && options.emitCss !== false && (compiled.css?.code?.trim().length ?? 0) > 0) {
|
|
93
|
+
compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`;
|
|
94
|
+
}
|
|
95
|
+
return { filename, normalizedFilename, cssId, compiled, ssr };
|
|
96
|
+
}
|
package/src/compiler.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Format } from 'vigla/compiler';
|
|
2
|
+
|
|
3
|
+
export interface Position {
|
|
4
|
+
line: number;
|
|
5
|
+
column: number;
|
|
6
|
+
character?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Warning {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
filename?: string;
|
|
13
|
+
start?: Position;
|
|
14
|
+
end?: Position;
|
|
15
|
+
frame?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CompileError extends Error, Warning {}
|
|
19
|
+
|
|
20
|
+
export interface SourceMap {
|
|
21
|
+
file?: string;
|
|
22
|
+
sources?: (string | null)[];
|
|
23
|
+
sourceRoot?: string;
|
|
24
|
+
mappings: unknown;
|
|
25
|
+
toUrl?: () => string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface Code {
|
|
29
|
+
code: string;
|
|
30
|
+
map: SourceMap | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Css extends Code {
|
|
34
|
+
hasGlobal: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CompileResult {
|
|
38
|
+
js: Code;
|
|
39
|
+
css: Css | null;
|
|
40
|
+
warnings: Warning[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CssHashInput {
|
|
44
|
+
name: string;
|
|
45
|
+
filename: string;
|
|
46
|
+
css: string;
|
|
47
|
+
hash: (input: string) => string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface Experimental {
|
|
51
|
+
async?: boolean;
|
|
52
|
+
[name: string]: unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ModuleCompileOptions {
|
|
56
|
+
filename?: string;
|
|
57
|
+
rootDir?: string;
|
|
58
|
+
dev?: boolean;
|
|
59
|
+
generate?: 'client' | 'server' | false;
|
|
60
|
+
warningFilter?: (warning: Warning) => boolean;
|
|
61
|
+
experimental?: Experimental;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CompileOptions extends ModuleCompileOptions {
|
|
65
|
+
css?: 'injected' | 'external';
|
|
66
|
+
cssHash?: (input: CssHashInput) => string;
|
|
67
|
+
hmr?: boolean;
|
|
68
|
+
sourcemap?: object | string;
|
|
69
|
+
[name: string]: unknown;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Processed {
|
|
73
|
+
code: string;
|
|
74
|
+
map?: string | object;
|
|
75
|
+
dependencies?: string[];
|
|
76
|
+
attributes?: Record<string, string | boolean>;
|
|
77
|
+
toString?: () => string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type MarkupPreprocessor = (options: {
|
|
81
|
+
content: string;
|
|
82
|
+
filename?: string;
|
|
83
|
+
}) => Processed | void | Promise<Processed | void>;
|
|
84
|
+
|
|
85
|
+
export type Preprocessor = (options: {
|
|
86
|
+
content: string;
|
|
87
|
+
attributes: Record<string, string | boolean>;
|
|
88
|
+
markup: string;
|
|
89
|
+
filename?: string;
|
|
90
|
+
}) => Processed | void | Promise<Processed | void>;
|
|
91
|
+
|
|
92
|
+
export interface PreprocessorGroup {
|
|
93
|
+
name?: string;
|
|
94
|
+
markup?: MarkupPreprocessor;
|
|
95
|
+
style?: Preprocessor;
|
|
96
|
+
script?: Preprocessor;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ScriptAttribute {
|
|
100
|
+
name: string;
|
|
101
|
+
value: true | readonly { data?: string }[] | object;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface Script {
|
|
105
|
+
attributes: readonly ScriptAttribute[];
|
|
106
|
+
content: { start: number; end: number; body: readonly object[] };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ParsedComponent {
|
|
110
|
+
module?: Script | null;
|
|
111
|
+
instance?: Script | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type Parse = (
|
|
115
|
+
source: string,
|
|
116
|
+
options: { modern: true; filename?: string }
|
|
117
|
+
) => ParsedComponent;
|
|
118
|
+
|
|
119
|
+
export interface Compiler {
|
|
120
|
+
readonly format?: Format;
|
|
121
|
+
readonly parse?: Parse;
|
|
122
|
+
compile(source: string, options: CompileOptions): CompileResult;
|
|
123
|
+
compileModule(source: string, options: ModuleCompileOptions): CompileResult;
|
|
124
|
+
preprocess(
|
|
125
|
+
source: string,
|
|
126
|
+
preprocessor: PreprocessorGroup | PreprocessorGroup[],
|
|
127
|
+
options?: { filename?: string }
|
|
128
|
+
): Promise<Processed>;
|
|
129
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import type { UserConfig } from 'vite';
|
|
6
|
+
import { frontEnds } from './front-end.ts';
|
|
7
|
+
import type { Identity } from './identity.ts';
|
|
8
|
+
import type { Config, Options } from './public.ts';
|
|
9
|
+
|
|
10
|
+
export type LoadedConfig = Partial<Config> & { configFile?: string };
|
|
11
|
+
|
|
12
|
+
export function configNames(stems: readonly string[]): string[] {
|
|
13
|
+
return stems.flatMap((stem) =>
|
|
14
|
+
['js', 'ts', 'mjs', 'mts'].map((extension) => `${stem}.${extension}`)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function find(
|
|
19
|
+
root: string,
|
|
20
|
+
inline: Partial<Options> | undefined,
|
|
21
|
+
stems: readonly string[],
|
|
22
|
+
{ log, messages }: Identity
|
|
23
|
+
): string | undefined {
|
|
24
|
+
if (inline?.configFile) {
|
|
25
|
+
const file = path.isAbsolute(inline.configFile)
|
|
26
|
+
? inline.configFile
|
|
27
|
+
: path.resolve(root, inline.configFile);
|
|
28
|
+
if (!fs.existsSync(file)) throw new Error(messages.configNotFound(file));
|
|
29
|
+
return file;
|
|
30
|
+
}
|
|
31
|
+
const existing = configNames(stems)
|
|
32
|
+
.map((name) => path.resolve(root, name))
|
|
33
|
+
.filter((file) => fs.existsSync(file));
|
|
34
|
+
if (existing.length === 0) {
|
|
35
|
+
log.info(messages.configAbsent(root));
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
if (existing.length > 1) {
|
|
39
|
+
log.warn(messages.configsMultiple(existing[0]!), existing);
|
|
40
|
+
}
|
|
41
|
+
return existing[0];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function loadConfigFile(
|
|
45
|
+
viteConfig: UserConfig | undefined,
|
|
46
|
+
inline: Partial<Options> | undefined,
|
|
47
|
+
stems: readonly string[],
|
|
48
|
+
identity: Identity
|
|
49
|
+
): Promise<LoadedConfig | undefined> {
|
|
50
|
+
if (inline?.configFile === false) return undefined;
|
|
51
|
+
const file = find(viteConfig?.root || process.cwd(), inline, stems, identity);
|
|
52
|
+
if (file === undefined) return undefined;
|
|
53
|
+
try {
|
|
54
|
+
const url = `${pathToFileURL(file).href}?t=${fs.statSync(file).mtimeMs}`;
|
|
55
|
+
const loaded = ((await import(url)) as { default?: Partial<Config> }).default;
|
|
56
|
+
if (loaded == null) throw new Error(`invalid export in ${file}`);
|
|
57
|
+
return { ...loaded, configFile: file };
|
|
58
|
+
} catch (error) {
|
|
59
|
+
identity.log.error(`failed to import config ${file}`, error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function loadConfig(
|
|
65
|
+
identity: Identity,
|
|
66
|
+
viteConfig?: UserConfig,
|
|
67
|
+
inline?: Partial<Options>,
|
|
68
|
+
stem?: string
|
|
69
|
+
): Promise<LoadedConfig | undefined> {
|
|
70
|
+
if (inline?.configFile === false) return undefined;
|
|
71
|
+
const root = viteConfig?.root || process.cwd();
|
|
72
|
+
const stems =
|
|
73
|
+
stem === undefined ? (await frontEnds(root)).map((frontEnd) => frontEnd.config) : [stem];
|
|
74
|
+
return loadConfigFile(viteConfig, inline, stems, identity);
|
|
75
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Names } from './identity.ts';
|
|
2
|
+
|
|
3
|
+
const withoutSources = [
|
|
4
|
+
'@lukeed/uuid',
|
|
5
|
+
'@playwright/test',
|
|
6
|
+
'autoprefixer',
|
|
7
|
+
'cookie',
|
|
8
|
+
'dotenv',
|
|
9
|
+
'esbuild',
|
|
10
|
+
'eslint',
|
|
11
|
+
'jest',
|
|
12
|
+
'mdsvex',
|
|
13
|
+
'playwright',
|
|
14
|
+
'postcss',
|
|
15
|
+
'prettier',
|
|
16
|
+
'tslib',
|
|
17
|
+
'typescript',
|
|
18
|
+
'vite',
|
|
19
|
+
'vitest',
|
|
20
|
+
'__vite-browser-external'
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const prefixesWithoutSources = [
|
|
24
|
+
'@fontsource/',
|
|
25
|
+
'@postcss-plugins/',
|
|
26
|
+
'@rollup/',
|
|
27
|
+
'@types/',
|
|
28
|
+
'@typescript-eslint/',
|
|
29
|
+
'eslint-',
|
|
30
|
+
'jest-',
|
|
31
|
+
'postcss-plugin-',
|
|
32
|
+
'prettier-plugin-',
|
|
33
|
+
'rollup-plugin-',
|
|
34
|
+
'vite-plugin-'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export function shipsNoSources(names: Names, dependency: string): boolean {
|
|
38
|
+
const exact = [...withoutSources, ...names.withoutSources];
|
|
39
|
+
return (
|
|
40
|
+
exact.includes(dependency) ||
|
|
41
|
+
[...prefixesWithoutSources, ...names.withoutSourcesPrefixes].some((prefix) =>
|
|
42
|
+
prefix.startsWith('@')
|
|
43
|
+
? dependency.startsWith(prefix)
|
|
44
|
+
: dependency.substring(dependency.lastIndexOf('/') + 1).startsWith(prefix)
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
}
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Rollup } from 'vite';
|
|
2
|
+
import type { CompileError, PreprocessorGroup } from './compiler.ts';
|
|
3
|
+
import type { Links } from './identity.ts';
|
|
4
|
+
import { buildExtendedLogMessage } from './log.ts';
|
|
5
|
+
import { arraify, type ResolvedOptions } from './options.ts';
|
|
6
|
+
|
|
7
|
+
const styleTag =
|
|
8
|
+
/<!--[^]*?-->|<style((?:\s+[^=>'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/]+)*\s*)(?:\/>|>([\S\s]*?)<\/style>)/g;
|
|
9
|
+
|
|
10
|
+
function viteFrame(frame: string | undefined): string {
|
|
11
|
+
if (!frame) return '';
|
|
12
|
+
return frame
|
|
13
|
+
.split('\n')
|
|
14
|
+
.map((line) => (/^\s+\^/.test(line) ? ` ${line}` : ` ${line.replace(':', ' | ')}`))
|
|
15
|
+
.join('\n');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function toRollupError(error: CompileError, options: ResolvedOptions): Rollup.RollupError {
|
|
19
|
+
const { filename, frame, start, code, name, stack } = error;
|
|
20
|
+
const converted: Rollup.RollupError = {
|
|
21
|
+
name,
|
|
22
|
+
id: filename,
|
|
23
|
+
message: buildExtendedLogMessage(error),
|
|
24
|
+
frame: viteFrame(frame),
|
|
25
|
+
code,
|
|
26
|
+
stack: options.isBuild || options.isDebug || !frame ? stack : ''
|
|
27
|
+
};
|
|
28
|
+
if (start) converted.loc = { line: start.line, column: start.column, file: filename };
|
|
29
|
+
return converted;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fixableByCssPreprocessor(code: string | undefined): boolean {
|
|
33
|
+
return code === 'expected_token' || code === 'unexpected_eof' || !!code?.startsWith('css_');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function enhanceCompileError(
|
|
37
|
+
error: CompileError,
|
|
38
|
+
source: string,
|
|
39
|
+
preprocess: PreprocessorGroup | PreprocessorGroup[] | undefined,
|
|
40
|
+
links: Links
|
|
41
|
+
): CompileError {
|
|
42
|
+
const groups = arraify(preprocess);
|
|
43
|
+
const notes: string[] = [];
|
|
44
|
+
if (fixableByCssPreprocessor(error.code)) {
|
|
45
|
+
for (const match of source.matchAll(styleTag)) {
|
|
46
|
+
if (match[0].startsWith('<!--')) continue;
|
|
47
|
+
if (!match[1]?.includes('lang=')) {
|
|
48
|
+
notes.push('Did you forget to add a lang attribute to your style tag?');
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
groups.every(
|
|
52
|
+
(group) => group.style == null || group.name === 'inject-scope-everything-rule'
|
|
53
|
+
)
|
|
54
|
+
) {
|
|
55
|
+
const lang = match[1]?.match(/lang="(.+?)"/)?.[1] ?? 'style';
|
|
56
|
+
notes.push(
|
|
57
|
+
`Did you forget to add a ${lang} preprocessor?${links.preprocess ? ` See ${links.preprocess} for more information.` : ''}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (notes.length > 0) error.message += `\n\n- ${notes.join('\n- ')}`;
|
|
63
|
+
return error;
|
|
64
|
+
}
|
package/src/front-end.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { searchForWorkspaceRoot } from 'vite';
|
|
5
|
+
import { findDepPkgJsonPath } from 'vitefu';
|
|
6
|
+
import type { Compiler } from './compiler.ts';
|
|
7
|
+
|
|
8
|
+
export interface ModulePattern {
|
|
9
|
+
readonly infixes: readonly string[];
|
|
10
|
+
readonly extensions: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Declaration {
|
|
14
|
+
readonly compiler: string;
|
|
15
|
+
readonly components: readonly string[];
|
|
16
|
+
readonly modules: ModulePattern;
|
|
17
|
+
readonly condition: string;
|
|
18
|
+
readonly config: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface FrontEnd {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly compiler: Compiler;
|
|
24
|
+
readonly compilerImport: string;
|
|
25
|
+
readonly components: readonly string[];
|
|
26
|
+
readonly modules: ModulePattern;
|
|
27
|
+
readonly condition: string;
|
|
28
|
+
readonly config: string;
|
|
29
|
+
readonly imports: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface Manifest {
|
|
33
|
+
name?: string;
|
|
34
|
+
version?: string;
|
|
35
|
+
exports?: unknown;
|
|
36
|
+
dependencies?: Record<string, string>;
|
|
37
|
+
devDependencies?: Record<string, string>;
|
|
38
|
+
peerDependencies?: Record<string, string>;
|
|
39
|
+
vigla?: { frontEnd?: Declaration };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const conditions = ['node', 'import', 'default'];
|
|
43
|
+
const found = new Map<string, Promise<FrontEnd[]>>();
|
|
44
|
+
|
|
45
|
+
function read(file: string): Manifest {
|
|
46
|
+
return JSON.parse(readFileSync(file, 'utf8')) as Manifest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function target(entry: unknown): string | undefined {
|
|
50
|
+
if (typeof entry === 'string') return entry;
|
|
51
|
+
if (Array.isArray(entry)) {
|
|
52
|
+
for (const item of entry) {
|
|
53
|
+
const resolved = target(item);
|
|
54
|
+
if (resolved !== undefined) return resolved;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (entry === null || typeof entry !== 'object') return undefined;
|
|
59
|
+
for (const [condition, value] of Object.entries(entry)) {
|
|
60
|
+
if (!conditions.includes(condition)) continue;
|
|
61
|
+
const resolved = target(value);
|
|
62
|
+
if (resolved !== undefined) return resolved;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function typesOnly(entry: unknown): boolean {
|
|
68
|
+
return (
|
|
69
|
+
entry !== null &&
|
|
70
|
+
typeof entry === 'object' &&
|
|
71
|
+
!Array.isArray(entry) &&
|
|
72
|
+
Object.keys(entry).length === 1 &&
|
|
73
|
+
'types' in entry
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function exported(manifest: Manifest): Record<string, unknown> {
|
|
78
|
+
const exports = manifest.exports;
|
|
79
|
+
if (exports === undefined || exports === null) return {};
|
|
80
|
+
if (typeof exports === 'string' || Array.isArray(exports)) return { '.': exports };
|
|
81
|
+
const keys = Object.keys(exports);
|
|
82
|
+
if (keys.length > 0 && !keys[0]?.startsWith('.')) return { '.': exports };
|
|
83
|
+
return exports as Record<string, unknown>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function imports(name: string, manifest: Manifest): string[] {
|
|
87
|
+
return Object.entries(exported(manifest))
|
|
88
|
+
.filter(([key, entry]) => key !== './package.json' && !typesOnly(entry))
|
|
89
|
+
.map(([key]) => key.replace(/^\./, name));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function load(name: string, file: string, manifest: Manifest): Promise<FrontEnd> {
|
|
93
|
+
const declaration = manifest.vigla?.frontEnd as Declaration;
|
|
94
|
+
const entry = target(exported(manifest)[declaration.compiler]);
|
|
95
|
+
if (entry === undefined) {
|
|
96
|
+
throw new Error(`${name} declares compiler ${declaration.compiler} but exports no such entry`);
|
|
97
|
+
}
|
|
98
|
+
const url = pathToFileURL(path.join(path.dirname(file), entry)).href;
|
|
99
|
+
const compiler = (await import(url)) as Compiler;
|
|
100
|
+
return {
|
|
101
|
+
name,
|
|
102
|
+
compiler,
|
|
103
|
+
compilerImport: declaration.compiler.replace(/^\./, name),
|
|
104
|
+
components: declaration.components,
|
|
105
|
+
modules: declaration.modules,
|
|
106
|
+
condition: declaration.condition,
|
|
107
|
+
config: declaration.config,
|
|
108
|
+
imports: imports(name, manifest)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function dependencies(dir: string): string[] {
|
|
113
|
+
const file = path.join(dir, 'package.json');
|
|
114
|
+
if (!existsSync(file)) return [];
|
|
115
|
+
const manifest = read(file);
|
|
116
|
+
return [
|
|
117
|
+
...Object.keys(manifest.dependencies ?? {}),
|
|
118
|
+
...Object.keys(manifest.devDependencies ?? {})
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function discover(root: string): Promise<FrontEnd[]> {
|
|
123
|
+
const seen = new Set<string>();
|
|
124
|
+
const loading: Promise<FrontEnd>[] = [];
|
|
125
|
+
const workspace = searchForWorkspaceRoot(root);
|
|
126
|
+
for (const dir of workspace === root ? [root] : [root, workspace]) {
|
|
127
|
+
for (const name of dependencies(dir)) {
|
|
128
|
+
if (seen.has(name)) continue;
|
|
129
|
+
seen.add(name);
|
|
130
|
+
const file = await findDepPkgJsonPath(name, dir);
|
|
131
|
+
if (file === undefined) continue;
|
|
132
|
+
const manifest = read(file);
|
|
133
|
+
if (manifest.vigla?.frontEnd !== undefined) loading.push(load(name, file, manifest));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Promise.all(loading);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function frontEnds(root: string): Promise<FrontEnd[]> {
|
|
140
|
+
let pending = found.get(root);
|
|
141
|
+
if (pending === undefined) {
|
|
142
|
+
pending = discover(root);
|
|
143
|
+
found.set(root, pending);
|
|
144
|
+
}
|
|
145
|
+
return pending;
|
|
146
|
+
}
|