rollup#ModuleInfo TypeScript Examples
The following examples show how to use
rollup#ModuleInfo.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: pluginContainer.ts From icepkg with MIT License | 4 votes |
export async function createPluginContainer(
{ plugins, logger, root, output, build: { rollupOptions } },
moduleGraph?: any,
watcher?: FSWatcher,
): Promise<PluginContainer> {
let ids = 0; // counter for generating unique emitted asset IDs
const files = new Map();
const isDebug = process.env.DEBUG;
const seenResolves: Record<string, true | undefined> = {};
const debugResolve = createLogger('resolve');
const debugPluginResolve = createDebugger('plugin-resolve');
const debugPluginTransform = createDebugger('plugin-transform');
const watchFiles = new Set<string>();
// get rollup version
const rollupPkgPath = resolve(require.resolve('rollup'), '../../package.json');
const minimalContext: MinimalPluginContext = {
meta: {
rollupVersion: safeRequire(rollupPkgPath)
.version,
// rollupVersion: '2.3.4',
watchMode: true,
},
};
function warnIncompatibleMethod(method: string, plugin: string) {
logger.warn(
colors.cyan(`[plugin:${plugin}] `) +
colors.yellow(
`context method ${colors.bold(
`${method}()`,
)} is not supported in serve mode. This plugin is likely not vite-compatible.`,
),
);
}
// throw when an unsupported ModuleInfo property is accessed,
// so that incompatible plugins fail in a non-cryptic way.
const ModuleInfoProxy: ProxyHandler<ModuleInfo> = {
get(info: any, key: string) {
if (key in info) {
return info[key];
}
throw Error(
`[vite] The "${key}" property of ModuleInfo is not supported.`,
);
},
};
// same default value of "moduleInfo.meta" as in Rollup
const EMPTY_OBJECT = Object.freeze({});
function getModuleInfo(id: string) {
const module = moduleGraph?.getModuleById(id);
if (!module) {
return null;
}
if (!module.info) {
module.info = new Proxy(
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
{ id, meta: module.meta || EMPTY_OBJECT } as ModuleInfo,
ModuleInfoProxy,
);
}
return module.info;
}
function updateModuleInfo(id: string, { meta }: { meta?: object | null }) {
if (meta) {
const moduleInfo = getModuleInfo(id);
if (moduleInfo) {
moduleInfo.meta = { ...moduleInfo.meta, ...meta };
}
}
}
// we should create a new context for each async hook pipeline so that the
// active plugin in that pipeline can be tracked in a concurrency-safe manner.
// using a class to make creating new contexts more efficient
class Context implements PluginContext {
meta = minimalContext.meta;
ssr = false;
_activePlugin: Plugin | null;
_activeId: string | null = null;
_activeCode: string | null = null;
_resolveSkips?: Set<Plugin>;
_addedImports: Set<string> | null = null;
constructor(initialPlugin?: Plugin) {
this._activePlugin = initialPlugin || null;
}
parse(code: string, opts: any = {}) {
return parser.parse(code, {
sourceType: 'module',
ecmaVersion: 'latest',
locations: true,
...opts,
});
}
async resolve(
id: string,
importer?: string,
options?: { skipSelf?: boolean },
) {
let skip: Set<Plugin> | undefined;
if (options?.skipSelf && this._activePlugin) {
skip = new Set(this._resolveSkips);
skip.add(this._activePlugin);
}
let out = await container.resolveId(id, importer, { skip, ssr: this.ssr });
if (typeof out === 'string') out = { id: out };
return out as ResolvedId | null;
}
getModuleInfo(id: string) {
return getModuleInfo(id);
}
getModuleIds() {
return moduleGraph
? moduleGraph.idToModuleMap.keys()
: Array.prototype[Symbol.iterator]();
}
addWatchFile(id: string) {
watchFiles.add(id);
(this._addedImports || (this._addedImports = new Set())).add(id);
if (watcher) ensureWatchedFile(watcher, id, root);
}
getWatchFiles() {
return [...watchFiles];
}
emitFile(assetOrFile: EmittedFile) {
function resolveFileName(fileName: string) {
if (!fileName) return;
if (isAbsolute(fileName)) return fileName;
return resolve(root, output, fileName);
}
const { type, fileName } = assetOrFile;
const name = type === 'chunk' ? assetOrFile.name || assetOrFile.id : assetOrFile.name;
const source = type === 'asset' && assetOrFile.source;
const filename = resolveFileName(fileName);
const id = String(++ids);
files.set(id, { id, name, filename });
if (type === 'chunk') {
consola.warn(`type ${type} of this.emitFile is not supported in transform mode. This plugin is likely not compatible`);
} else if (source) {
fs.writeFileSync(filename, source);
}
return id;
}
setAssetSource(assetId: string, source: string | Uint8Array) {
const asset = files.get(String(assetId));
if (asset.type === 'chunk') {
return;
}
asset.source = source;
fs.writeFile(asset.filename, source);
}
getFileName() {
warnIncompatibleMethod('getFileName', this._activePlugin!.name);
return '';
}
warn(
e: string | RollupError,
position?: number | { column: number; line: number },
) {
const err = formatError(e, position, this);
const msg = err.message;
// const msg = buildErrorMessage(
// err,
// [colors.yellow(`warning: ${err.message}`)],
// false,
// );
logger.warn(msg, {
clear: true,
timestamp: true,
});
}
error(
e: string | RollupError,
position?: number | { column: number; line: number },
): never {
// error thrown here is caught by the transform middleware and passed on
// the the error middleware.
throw formatError(e, position, this);
}
}
function formatError(
e: string | RollupError,
position: number | { column: number; line: number } | undefined,
ctx: Context,
) {
let err = (
typeof e === 'string' ? new Error(e) : e
) as postcss.CssSyntaxError & RollupError;
if (err.file && err.name === 'CssSyntaxError') {
err.id = normalizePath(err.file);
}
if (ctx._activePlugin) err.plugin = ctx._activePlugin.name;
if (ctx._activeId && !err.id) err.id = ctx._activeId;
if (ctx._activeCode && !err.pluginCode) {
err.pluginCode = ctx._activeCode;
const pos =
position != null
? position
: err.pos != null
? err.pos
: // some rollup plugins, e.g. json, sets position instead of pos
(err as any).position;
if (pos != null) {
let errLocation;
try {
errLocation = numberToPos(ctx._activeCode, pos);
} catch (err2) {
logger.error(
colors.red(
`Error in error handler:\n${err2.stack || err2.message}\n`,
),
// print extra newline to separate the two errors
{ error: err2 },
);
throw err;
}
err.loc = err.loc || {
file: err.id,
...errLocation,
};
err.frame = err.frame || generateCodeFrame(ctx._activeCode, pos);
} else if (err.loc) {
// css preprocessors may report errors in an included file
if (!err.frame) {
let code = ctx._activeCode;
if (err.loc.file) {
err.id = normalizePath(err.loc.file);
try {
code = fs.readFileSync(err.loc.file, 'utf-8');
} catch {
// empty
}
}
err.frame = generateCodeFrame(code, err.loc);
}
} else if ((err as any).line && (err as any).column) {
err.loc = {
file: err.id,
line: (err as any).line,
column: (err as any).column,
};
err.frame = err.frame || generateCodeFrame(err.id!, err.loc);
}
if (err.loc && ctx instanceof TransformContext) {
const rawSourceMap = ctx._getCombinedSourcemap();
if (rawSourceMap) {
const consumer = new SourceMapConsumer(rawSourceMap as any);
const { source, line, column } = consumer.originalPositionFor({
line: Number(err.loc.line),
column: Number(err.loc.column),
bias: SourceMapConsumer.GREATEST_LOWER_BOUND,
});
if (source) {
err.loc = { file: source, line, column };
}
}
}
}
// Be consistent of rollup https://github.com/rollup/rollup/blob/master/src/utils/pluginUtils.ts#L7
if (!(err instanceof Error)) {
err = Object.assign(new Error(err.message), err);
}
return err;
}
class TransformContext extends Context {
filename: string;
originalCode: string;
originalSourcemap: SourceMap | null = null;
sourcemapChain: Array<NonNullable<SourceDescription['map']>> = [];
combinedMap: SourceMap | null = null;
constructor(filename: string, code: string, inMap?: SourceMap | string) {
super();
this.filename = filename;
this.originalCode = code;
if (inMap) {
this.sourcemapChain.push(inMap);
}
}
_getCombinedSourcemap(createIfNull = false) {
let { combinedMap } = this;
for (let m of this.sourcemapChain) {
if (typeof m === 'string') m = JSON.parse(m);
if (!('version' in (m as SourceMap))) {
// empty, nullified source map
this.combinedMap = null;
combinedMap = this.combinedMap;
this.sourcemapChain.length = 0;
break;
}
if (!combinedMap) {
combinedMap = m as SourceMap;
} else {
combinedMap = combineSourcemaps(this.filename, [
{
...(m as RawSourceMap),
sourcesContent: combinedMap.sourcesContent,
},
combinedMap as RawSourceMap,
]) as SourceMap;
}
}
if (!combinedMap) {
return createIfNull
? new MagicString(this.originalCode).generateMap({
includeContent: true,
hires: true,
source: this.filename,
})
: null;
}
if (combinedMap !== this.combinedMap) {
this.combinedMap = combinedMap;
this.sourcemapChain.length = 0;
}
return this.combinedMap;
}
getCombinedSourcemap() {
return this._getCombinedSourcemap(true) as SourceMap;
}
}
let closed = false;
const container: PluginContainer = {
options: await (async () => {
let options = rollupOptions;
for (const plugin of plugins) {
if (!plugin.options) continue;
options =
(await plugin.options.call(minimalContext, options)) || options;
}
if (options.acornInjectPlugins) {
parser = acorn.Parser.extend(options.acornInjectPlugins as any);
}
return {
acorn,
acornInjectPlugins: [],
...options,
};
})(),
getModuleInfo,
async buildStart() {
await Promise.all(
plugins.map((plugin) => {
if (plugin.buildStart) {
return plugin.buildStart.call(
new Context(plugin) as any,
container.options as NormalizedInputOptions,
);
}
return null;
}),
);
},
async resolveId(rawId, importer = join(root, 'index.html'), options) {
const skip = options?.skip;
const ssr = options?.ssr;
const ctx = new Context();
ctx.ssr = !!ssr;
ctx._resolveSkips = skip;
const resolveStart = isDebug ? performance.now() : 0;
let id: string | null = null;
const partial: Partial<PartialResolvedId> = {};
for (const plugin of plugins) {
if (!plugin.resolveId) continue;
if (skip?.has(plugin)) continue;
ctx._activePlugin = plugin;
const pluginResolveStart = isDebug ? performance.now() : 0;
const result = await plugin.resolveId.call(
ctx as any,
rawId,
importer,
{ ssr },
);
if (!result) continue;
if (typeof result === 'string') {
id = result;
} else {
id = result.id;
Object.assign(partial, result);
}
isDebug &&
debugPluginResolve.info(
timeFrom(pluginResolveStart),
plugin.name,
// prettifyUrl(id, root),
);
// resolveId() is hookFirst - first non-null result is returned.
break;
}
if (isDebug && rawId !== id && !rawId.startsWith(FS_PREFIX)) {
const key = rawId + id;
// avoid spamming
if (!seenResolves[key]) {
seenResolves[key] = true;
debugResolve.info(
`${timeFrom(resolveStart)}`,
`${colors.cyan(rawId)} -> ${colors.dim(
id,
)}`,
);
}
}
if (id) {
partial.id = isExternalUrl(id) ? id : normalizePath(id);
return partial as PartialResolvedId;
} else {
return null;
}
},
async load(id, options) {
const ssr = options?.ssr;
const ctx = new Context();
ctx.ssr = !!ssr;
for (const plugin of plugins) {
if (!plugin.load) continue;
ctx._activePlugin = plugin;
// eslint-disable-next-line no-await-in-loop
const result = await plugin.load.call(ctx as any, id, { ssr });
if (result != null) {
if (isObject(result)) {
updateModuleInfo(id, result);
}
return result;
}
}
return null;
},
async transform(code, id, options) {
const inMap = options?.inMap;
const ssr = options?.ssr;
const ctx = new TransformContext(id, code, inMap as SourceMap);
ctx.ssr = !!ssr;
let meta = null;
for (const plugin of plugins) {
if (!plugin.transform) continue;
ctx._activePlugin = plugin;
ctx._activeId = id;
ctx._activeCode = code;
const start = isDebug ? performance.now() : 0;
let result: TransformResult | string | undefined;
try {
// eslint-disable-next-line no-await-in-loop
result = await plugin.transform.call(ctx as any, code, id, { ssr });
} catch (e) {
ctx.error(e);
}
if (!result) continue;
isDebug &&
debugPluginTransform(
timeFrom(start),
plugin.name,
// prettifyUrl(id, root),
);
if (isObject(result)) {
if (result.code !== undefined) {
code = result.code;
if (result.map) {
ctx.sourcemapChain.push(result.map);
}
// Add to support custom meta info
if (result.meta) {
meta = result.meta;
}
}
updateModuleInfo(id, result);
} else {
code = result;
}
}
return {
code,
map: ctx._getCombinedSourcemap(),
meta,
};
},
async close() {
if (closed) return;
const ctx = new Context();
await Promise.all(
plugins.map((p) => p.buildEnd && p.buildEnd.call(ctx as any)),
);
await Promise.all(
plugins.map((p) => p.closeBundle && p.closeBundle.call(ctx as any)),
);
closed = true;
},
};
return container;
}
Example #2
Source File: createRollupPluginContextAdapter.ts From web with MIT License | 4 votes |
export function createRollupPluginContextAdapter<
T extends PluginContext | MinimalPluginContext | TransformPluginContext,
>(
pluginContext: T,
wdsPlugin: WdsPlugin,
config: DevServerCoreConfig,
fileWatcher: FSWatcher,
context: Context,
pluginMetaPerModule: Map<string, CustomPluginOptions>,
) {
return {
...pluginContext,
getModuleInfo(id: string): ModuleInfo {
return {
id,
code: context.body as string,
ast: null,
dynamicallyImportedIds: [],
dynamicallyImportedIdResolutions: [],
dynamicImporters: [],
hasDefaultExport: false,
implicitlyLoadedBefore: [],
implicitlyLoadedAfterOneOf: [],
importedIds: [],
importedIdResolutions: [],
importers: [],
isEntry: false,
isExternal: false,
isIncluded: false,
hasModuleSideEffects: false,
syntheticNamedExports: false,
meta: pluginMetaPerModule.get(id) ?? {},
};
},
addWatchFile(id: string) {
const filePath = path.join(process.cwd(), id);
fileWatcher.add(filePath);
},
emitAsset() {
throw new Error('Emitting files is not yet supported');
},
emitFile() {
throw new Error('Emitting files is not yet supported');
},
getAssetFileName() {
throw new Error('Emitting files is not yet supported');
},
getFileName() {
throw new Error('Emitting files is not yet supported');
},
setAssetSource() {
throw new Error('Emitting files is not yet supported');
},
async resolve(source: string, importer: string, options: { skipSelf: boolean }) {
if (!context) throw new Error('Context is required.');
for (const pl of config.plugins ?? []) {
if (
pl.resolveImport &&
(!options.skipSelf || pl.resolveImport !== wdsPlugin.resolveImport)
) {
const result = await pl.resolveImport({ source, context });
let resolvedId: string | undefined;
if (typeof result === 'string') {
resolvedId = result;
} else if (typeof result === 'object') {
resolvedId = result?.id;
}
if (resolvedId) {
const importerDir = path.dirname(importer);
return {
id: path.isAbsolute(resolvedId) ? resolvedId : path.join(importerDir, resolvedId),
};
}
}
}
},
async resolveId(source: string, importer: string, options: { skipSelf: boolean }) {
const resolveResult = await this.resolve(source, importer, options);
if (typeof resolveResult === 'string') {
return resolveResult;
}
if (typeof resolveResult === 'object') {
return resolveResult?.id;
}
return resolveResult;
},
};
}