import {
posix as Path
} from 'path'
import {
loadModuleInternal
} from './tools'
import {
ModuleExport,
PathHandlers,
Options,
File,
Resource,
PathContext,
LangProcessor,
} from './types'
/**
* the version of the library (process.env.VERSION is set by webpack, at compile-time)
*/
export const version : string = process.env.VERSION;
/**
* the version of Vue that is expected by the library
*/
export { vueVersion } from './createSFCModule'
/**
* @internal
*/
function throwNotDefined(details : string) : never {
throw new ReferenceError(`${ details } is not defined`);
}
/**
* Default implementation of PathHandlers
*/
const defaultPathHandlers : PathHandlers = {
extname(filepath) {
return Path.extname(filepath);
},
resolve({ refPath, relPath } : PathContext) {
// note :
// normalize('./test') -> 'test'
// normalize('/test') -> '/test'
return (relPath[0] !== '.' && relPath[0] !== '/') ? relPath : Path.normalize(Path.join(Path.dirname(refPath), relPath));
}
}
function defaultGetResource(pathCx : PathContext, options : Options) : Resource {
const { pathHandlers: { resolve }, getFile } = options;
const path = resolve(pathCx);
return {
id: path,
path: path,
getContent: () => getFile(path),
};
}
/**
* This is the main function.
* This function is intended to be used only to load the entry point of your application.
* If for some reason you need to use it in your components, be sure to share at least the options.`compiledCache` object between all calls.
*
* @param path The path of the `.vue` file. If path is not a path (eg. an string ID), your [[getFile]] function must return a [[File]] object.
* @param options The options
* @returns A Promise of the component
*
* **example using `Vue.defineAsyncComponent`:**
*
* ```javascript
*
* const app = Vue.createApp({
* components: {
* 'my-component': Vue.defineAsyncComponent( () => loadModule('./myComponent.vue', options) )
* },
* template: ''
* });
*
* ```
*
* **example using `await`:**
*
* ```javascript
* ;(async () => {
*
* const app = Vue.createApp({
* components: {
* 'my-component': await loadModule('./myComponent.vue', options)
* },
* template: ''
* });
*
* })()
* .catch(ex => console.error(ex));
*
* ```
*
*/
export async function loadModule(path : string, options : Options = throwNotDefined('options')) : Promise {
const {
moduleCache = throwNotDefined('options.moduleCache'),
getFile = throwNotDefined('options.getFile()'),
addStyle = throwNotDefined('options.addStyle()'),
pathHandlers = defaultPathHandlers,
getResource = defaultGetResource,
} = options;
// moduleCache should be defined with Object.create(null). require('constructor') etc... should not be a default module
if ( moduleCache instanceof Object )
Object.setPrototypeOf(moduleCache, null);
// TBD: remove this in v1.0
async function normalizedGetFile(path : string) : Promise {
const res = await getFile(path);
return typeof res === 'object' ? res : { content: res, extname: pathHandlers.extname(path) };
}
const normalizedOptions = {
moduleCache,
pathHandlers,
getResource,
...options,
getFile: normalizedGetFile,
};
return await loadModuleInternal( { refPath: '/', relPath: path }, normalizedOptions);
}
/**
* Convert a function to template processor interface (consolidate)
*/
export function buildTemplateProcessor(processor: LangProcessor) {
return {
render: (source: string, preprocessOptions: string, cb: (_err : any, _res : any) => void) => {
try {
const ret = processor(source, preprocessOptions)
if (typeof ret === 'string') {
cb(null, ret)
} else {
ret.then(processed => {
cb(null, processed)
})
ret.catch(err => {
cb(err, null)
})
}
} catch (err) {
cb(err, null)
}
}
}
}