Skip to content

Commit e77324a

Browse files
wip(core)!: add Resource, rename additionalModuleHandlers into moduleHandlers, refactoring
- Use Resource as intermediate abstraction between file and module, this will allow to pre-process resources like webpack loaders does. - refactoring loadModule() that now uses loadModuleInternal() ...internally. - Options.additionalModuleHandlers has been renamed into Options.moduleHandlers because Options.moduleHandlers now holds all module handlers.
1 parent 4c7f974 commit e77324a

File tree

7 files changed

+159
-109
lines changed

7 files changed

+159
-109
lines changed

src/createSFCModule.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { LoadModule, ModuleExport, Options } from './types'
1+
import { ModuleExport, Options } from './types'
22

3-
export declare function createSFCModule (source: string, filename: string, options: Options, loadModule: LoadModule): Promise<ModuleExport>
3+
export declare function createSFCModule (source: string, filename: string, options: Options): Promise<ModuleExport>
44
export declare const vueVersion : string

src/createVue2SFCModule.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ import {
3939
transformJSCode,
4040
loadDeps,
4141
createModule,
42-
formatErrorLineColumn
42+
formatErrorLineColumn,
43+
loadModuleInternal,
4344
} from './tools'
4445

4546
import {
4647
Options,
47-
LoadModule,
4848
ModuleExport,
4949
CustomBlockCallback
5050
} from './types'
@@ -76,12 +76,12 @@ const isProd : boolean = process.env.NODE_ENV === 'production';
7676
* @internal
7777
*/
7878

79-
export async function createSFCModule(source : string, filename : string, options : Options, loadModule : LoadModule) : Promise<ModuleExport> {
79+
export async function createSFCModule(source : string, filename : string, options : Options) : Promise<ModuleExport> {
8080

8181
const component = {};
8282

8383

84-
const { delimiters, moduleCache, compiledCache, pathHandlers: { resolve }, getFile, addStyle, log, additionalBabelPlugins = [], customBlockHandler } = options;
84+
const { delimiters, moduleCache, compiledCache, getResource, addStyle, log, additionalBabelPlugins = [], customBlockHandler } = options;
8585

8686
const descriptor = sfc_parse({
8787
source,
@@ -97,7 +97,7 @@ export async function createSFCModule(source : string, filename : string, option
9797

9898
// hack: asynchronously preloads the language processor before it is required by the synchronous preprocessCustomRequire() callback, see below
9999
if ( descriptor.template && descriptor.template.lang )
100-
await loadModule(descriptor.template.lang, options);
100+
await loadModuleInternal(filename, descriptor.template.lang, options);
101101

102102

103103
const hasScoped = descriptor.styles.some(e => e.scoped);
@@ -109,7 +109,7 @@ export async function createSFCModule(source : string, filename : string, option
109109

110110
const compileTemplateOptions : TemplateCompileOptions = descriptor.template ? {
111111
// hack, since sourceMap is not configurable an we want to get rid of source-map dependency. see genSourcemap
112-
source: descriptor.template.src ? (await getFile(resolve(filename, descriptor.template.src))).content : descriptor.template.content,
112+
source: descriptor.template.src ? (await getResource(filename, descriptor.template.src, options).getContent()).content.toString() : descriptor.template.content,
113113
filename,
114114
compiler: vueTemplateCompiler as VueTemplateCompiler,
115115
compilerOptions: {
@@ -144,7 +144,7 @@ export async function createSFCModule(source : string, filename : string, option
144144

145145
// eg: https://github.com/vuejs/vue-loader/blob/v15.9.6/lib/index.js
146146

147-
const src = descriptor.script.src ? (await getFile(resolve(filename, descriptor.script.src))).content : descriptor.script.content;
147+
const src = descriptor.script.src ? (await getResource(filename, descriptor.script.src, options).getContent()).content.toString() : descriptor.script.content;
148148

149149
const [ depsList, transformedScriptSource ] = await withCache(compiledCache, [ componentHash, src ], async ({ preventCache }) => {
150150

@@ -188,8 +188,8 @@ export async function createSFCModule(source : string, filename : string, option
188188
return [ depsList, transformedScript.code ];
189189
});
190190

191-
await loadDeps(filename, depsList, options, loadModule);
192-
Object.assign(component, interopRequireDefault(createModule(filename, transformedScriptSource, options, loadModule).exports).default);
191+
await loadDeps(filename, depsList, options);
192+
Object.assign(component, interopRequireDefault(createModule(filename, transformedScriptSource, options).exports).default);
193193
}
194194

195195

@@ -232,14 +232,14 @@ export async function createSFCModule(source : string, filename : string, option
232232
return await transformJSCode(template.code, true, filename, options);
233233
});
234234

235-
await loadDeps(filename, templateDepsList, options, loadModule);
236-
Object.assign(component, createModule(filename, templateTransformedSource, options, loadModule).exports);
235+
await loadDeps(filename, templateDepsList, options);
236+
Object.assign(component, createModule(filename, templateTransformedSource, options).exports);
237237
}
238238

239239

240240
for ( const descStyle of descriptor.styles ) {
241241

242-
const src = descStyle.src ? (await getFile(resolve(filename, descStyle.src))).content : descStyle.content;
242+
const src = descStyle.src ? (await getResource(filename, descStyle.src, options).getContent()).content.toString() : descStyle.content;
243243

244244
const style = await withCache(compiledCache, [ componentHash, src, descStyle.lang ], async ({ preventCache }) => {
245245

@@ -259,7 +259,7 @@ export async function createSFCModule(source : string, filename : string, option
259259

260260
// Vue2 doesn't support preprocessCustomRequire, so we have to preprocess manually
261261
if ( descStyle.lang && processors[descStyle.lang] === undefined )
262-
processors[descStyle.lang] = await loadModule(descStyle.lang, options) as StylePreprocessor;
262+
processors[descStyle.lang] = await loadModuleInternal(filename, descStyle.lang, options) as StylePreprocessor;
263263

264264
const compiledStyle = await sfc_compileStyleAsync(compileStyleOptions);
265265
if ( compiledStyle.errors.length ) {

src/createVue3SFCModule.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ import {
4040
interopRequireDefault,
4141
transformJSCode,
4242
loadDeps,
43-
createModule
43+
createModule,
44+
loadModuleInternal,
4445
} from './tools'
4546

4647
import {
4748
Options,
48-
LoadModule,
4949
ModuleExport,
5050
CustomBlockCallback
5151
} from './types'
@@ -76,12 +76,12 @@ const isProd : boolean = process.env.NODE_ENV === 'production';
7676
* @internal
7777
*/
7878

79-
export async function createSFCModule(source : string, filename : string, options : Options, loadModule : LoadModule) : Promise<ModuleExport> {
79+
export async function createSFCModule(source : string, filename : string, options : Options) : Promise<ModuleExport> {
8080

8181
const component = {};
8282

8383

84-
const { delimiters, moduleCache, compiledCache, pathHandlers: { resolve }, getFile, addStyle, log, additionalBabelPlugins = [], customBlockHandler } = options;
84+
const { delimiters, moduleCache, compiledCache, getResource, addStyle, log, additionalBabelPlugins = [], customBlockHandler } = options;
8585

8686
// vue-loader next: https://github.com/vuejs/vue-loader/blob/next/src/index.ts#L91
8787
const { descriptor, errors } = sfc_parse(source, {
@@ -97,15 +97,15 @@ export async function createSFCModule(source : string, filename : string, option
9797

9898
// hack: asynchronously preloads the language processor before it is required by the synchronous preprocessCustomRequire() callback, see below
9999
if ( descriptor.template && descriptor.template.lang )
100-
await loadModule(descriptor.template.lang, options);
100+
await loadModuleInternal(filename, descriptor.template.lang, options);
101101

102102

103103
const hasScoped = descriptor.styles.some(e => e.scoped);
104104

105105
const compileTemplateOptions : SFCTemplateCompileOptions = descriptor.template ? {
106106
// hack, since sourceMap is not configurable an we want to get rid of source-map dependency. see genSourcemap
107107
compiler: { ...vue_CompilerDOM, compile: (template, options) => vue_CompilerDOM.compile(template, { ...options, sourceMap: genSourcemap }) },
108-
source: descriptor.template.src ? (await getFile(resolve(filename, descriptor.template.src))).content : descriptor.template.content,
108+
source: descriptor.template.src ? (await getResource(filename, descriptor.template.src, options).getContent()).content.toString() : descriptor.template.content,
109109
filename: descriptor.filename,
110110
isProd,
111111
scoped: hasScoped,
@@ -127,7 +127,7 @@ export async function createSFCModule(source : string, filename : string, option
127127
// doc: <script setup> cannot be used with the src attribute.
128128
// TBD: check if this is the right solution
129129
if ( descriptor.script?.src )
130-
descriptor.script.content = (await getFile(resolve(filename, descriptor.script.src))).content;
130+
descriptor.script.content = (await getResource(filename, descriptor.script.src, options).getContent()).content.toString();
131131

132132
// TBD: handle <script setup src="...
133133

@@ -200,8 +200,8 @@ export async function createSFCModule(source : string, filename : string, option
200200
return [ depsList, transformedScript.code ];
201201
});
202202

203-
await loadDeps(filename, depsList, options, loadModule);
204-
Object.assign(component, interopRequireDefault(createModule(filename, transformedScriptSource, options, loadModule).exports).default);
203+
await loadDeps(filename, depsList, options);
204+
Object.assign(component, interopRequireDefault(createModule(filename, transformedScriptSource, options).exports).default);
205205
}
206206

207207

@@ -234,18 +234,18 @@ export async function createSFCModule(source : string, filename : string, option
234234
return await transformJSCode(template.code, true, descriptor.filename, options);
235235
});
236236

237-
await loadDeps(filename, templateDepsList, options, loadModule);
238-
Object.assign(component, createModule(filename, templateTransformedSource, options, loadModule).exports);
237+
await loadDeps(filename, templateDepsList, options);
238+
Object.assign(component, createModule(filename, templateTransformedSource, options).exports);
239239
}
240240

241241

242242
for ( const descStyle of descriptor.styles ) {
243243

244244
// hack: asynchronously preloads the language processor before it is required by the synchronous preprocessCustomRequire() callback, see below
245245
if ( descStyle.lang )
246-
await loadModule(descStyle.lang, options);
246+
await loadModuleInternal(filename, descStyle.lang, options);
247247

248-
const src = descStyle.src ? (await getFile(resolve(filename, descStyle.src))).content : descStyle.content;
248+
const src = descStyle.src ? (await getResource(filename, descStyle.src, options).getContent()).content.toString() : descStyle.content;
249249

250250
const style = await withCache(compiledCache, [ componentHash, src ], async ({ preventCache }) => {
251251

src/index.ts

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { posix as Path } from 'path'
22

3-
import { createJSModule } from './tools'
3+
import { createJSModule, Loading, loadModuleInternal } from './tools'
44
import { createSFCModule, vueVersion } from './createSFCModule'
55

6-
import { ModuleExport, ModuleHandler, PathHandlers, Options, File } from './types'
6+
import { ModuleExport, ModuleHandler, PathHandlers, Options, File, Resource } from './types'
77

88
/**
99
* the version of the library (process.env.VERSION is set by webpack, at compile-time)
@@ -17,19 +17,6 @@ export const version : string = process.env.VERSION;
1717
export { vueVersion } from './createSFCModule'
1818

1919

20-
/**
21-
* @internal
22-
*/
23-
class Loading {
24-
25-
promise : Promise<ModuleExport>;
26-
27-
constructor(promise : Promise<ModuleExport>) {
28-
29-
this.promise = promise;
30-
}
31-
}
32-
3320

3421
/**
3522
* @internal
@@ -44,9 +31,9 @@ function throwNotDefined(details : string) : never {
4431
* @internal
4532
*/
4633
const defaultModuleHandlers : Record<string, ModuleHandler> = {
47-
'.vue': (source, path, options) => createSFCModule(source, path, options, loadModule),
48-
'.js': (source, path, options) => createJSModule(source, false, path, options, loadModule),
49-
'.mjs': (source, path, options) => createJSModule(source, true, path, options, loadModule),
34+
'.vue': (source, path, options) => createSFCModule(source, path, options),
35+
'.js': (source, path, options) => createJSModule(source, false, path, options),
36+
'.mjs': (source, path, options) => createJSModule(source, true, path, options),
5037
};
5138

5239

@@ -65,6 +52,18 @@ const defaultPathHandlers : PathHandlers = {
6552
}
6653

6754

55+
function defaultGetResource(currentResourcePath : string, depResourcePath : string, options : Options) : Resource {
56+
57+
const { pathHandlers: { resolve }, getFile } = options;
58+
const path = resolve(currentResourcePath, depResourcePath);
59+
return {
60+
id: path,
61+
path: path,
62+
getContent: async () => await getFile(path),
63+
};
64+
}
65+
66+
6867
/**
6968
* This is the main function.
7069
* This function is intended to be used only to load the entry point of your application.
@@ -112,8 +111,9 @@ export async function loadModule(path : string, options_ : Options = throwNotDef
112111
moduleCache = Object.create(null),
113112
getFile = throwNotDefined('options.getFile()'),
114113
addStyle = throwNotDefined('options.addStyle()'),
115-
additionalModuleHandlers = null,
114+
moduleHandlers = null,
116115
pathHandlers = defaultPathHandlers,
116+
getResource = defaultGetResource,
117117
loadModule,
118118
} = options_;
119119

@@ -127,45 +127,12 @@ export async function loadModule(path : string, options_ : Options = throwNotDef
127127

128128
const options = {
129129
moduleCache,
130-
additionalModuleHandlers,
131130
pathHandlers,
131+
getResource,
132132
...options_,
133133
getFile: normalizedGetFile,
134+
moduleHandlers: { ...defaultModuleHandlers, ...moduleHandlers },
134135
};
135136

136-
if ( path in moduleCache ) {
137-
138-
if ( moduleCache[path] instanceof Loading )
139-
return await moduleCache[path].promise;
140-
else
141-
return moduleCache[path];
142-
}
143-
144-
145-
moduleCache[path] = new Loading((async () => {
146-
147-
if ( loadModule ) {
148-
149-
const module = await loadModule(path, options);
150-
if ( module !== undefined )
151-
return moduleCache[path] = module;
152-
}
153-
154-
const file = await options.getFile(path);
155-
156-
const moduleHandlers = { ...defaultModuleHandlers, ...additionalModuleHandlers };
157-
158-
if ( !(file.extname in moduleHandlers) )
159-
throw new TypeError(`Unable to handle ${ file.extname } files (${ path }), see additionalModuleHandlers`);
160-
161-
if ( typeof file.content !== 'string' )
162-
throw new TypeError(`Invalid module content (${path}): ${ file.content }`);
163-
164-
const module = await moduleHandlers[file.extname](file.content, path, options);
165-
166-
return moduleCache[path] = module;
167-
168-
})());
169-
170-
return await moduleCache[path].promise;
137+
return await loadModuleInternal('', path, options);
171138
}

0 commit comments

Comments
 (0)