rollup#InputOptions TypeScript Examples
The following examples show how to use
rollup#InputOptions.
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: fromRollup.ts From web with MIT License | 6 votes |
export function fromRollup<T extends FnArgs>(
rollupPluginFn: RollupPluginFn<T>,
rollupInputOptions: Partial<InputOptions> = {},
options: FromRollupOptions = {},
) {
if (typeof rollupPluginFn !== 'function') {
throw new Error(
`fromRollup should be called with a rollup plugin function. Received: ${rollupPluginFn}`,
);
}
// return a function wrapper which intercepts creation of the rollup plugin
return function wrappedRollupPluginFn(...args: T) {
// call the original plugin function
const rollupPlugin = rollupPluginFn(...args);
// wrap the rollup plugin in an adapter for web dev server
return rollupAdapter(rollupPlugin, rollupInputOptions, options);
};
}
Example #2
Source File: createRollupPluginContexts.ts From web with MIT License | 5 votes |
/**
* Runs rollup with an empty module in order to capture the plugin context and
* normalized options.
* @param inputOptions
*/
export async function createRollupPluginContexts(
inputOptions: InputOptions,
): Promise<RollupPluginContexts> {
let normalizedInputOptions: NormalizedInputOptions | undefined = undefined;
let pluginContext: PluginContext | undefined = undefined;
let transformPluginContext: TransformPluginContext | undefined = undefined;
await rollup({
...inputOptions,
input: 'noop',
plugins: [
{
name: 'noop',
buildStart(options) {
normalizedInputOptions = options;
},
resolveId(id) {
pluginContext = this;
return id;
},
load() {
return '';
},
transform() {
transformPluginContext = this;
return null;
},
},
],
});
if (!normalizedInputOptions || !pluginContext || !transformPluginContext) {
throw new TypeError();
}
return {
normalizedInputOptions,
pluginContext,
transformPluginContext,
minimalPluginContext: { meta: (pluginContext as PluginContext).meta },
};
}
Example #3
Source File: addRollupInput.ts From web with MIT License | 5 votes |
export function addRollupInput(
inputOptions: InputOptions,
inputModuleIds: ScriptModuleTag[],
): InputOptions {
// Add input module ids to existing input option, whether it's a string, array or object
// this way you can use multiple html plugins all adding their own inputs
if (!inputOptions.input) {
return { ...inputOptions, input: inputModuleIds.map(mod => mod.importPath) };
}
if (typeof inputOptions.input === 'string') {
return {
...inputOptions,
input: [
...(inputOptions?.input?.endsWith('.html') ? [] : [inputOptions.input]),
...inputModuleIds.map(mod => mod.importPath),
],
};
}
if (Array.isArray(inputOptions.input)) {
return {
...inputOptions,
input: [...inputOptions.input, ...inputModuleIds.map(mod => mod.importPath)],
};
}
if (typeof inputOptions.input === 'object') {
return {
...inputOptions,
input: {
...inputOptions.input,
...fromEntries(
inputModuleIds
.map(mod => mod.importPath)
.map(i => [i.split('/').slice(-1)[0].split('.')[0], i]),
),
},
};
}
throw createError(`Unknown rollup input type. Supported inputs are string, array and object.`);
}
Example #4
Source File: plugins.ts From backstage with Apache License 2.0 | 4 votes |
/**
* This rollup plugin leaves all encountered asset imports as-is, but
* copies the imported files into the output directory.
*
* For example `import ImageUrl from './my-image.png'` inside `src/MyComponent` will
* cause `src/MyComponent/my-image.png` to be copied to the output directory at the
* path `dist/MyComponent/my-image.png`. The import itself will stay, but be resolved,
* resulting in something like `import ImageUrl from './MyComponent/my-image.png'`
*/
export function forwardFileImports(options: ForwardFileImportsOptions): Plugin {
const filter = createFilter(options.include, options.exclude);
// We collect the absolute paths to all files we want to bundle into the
// output dir here. Resolving to relative paths in the output dir happens later.
const exportedFiles = new Set<string>();
// We keep track of output directories that we've already copied files
// into, so that we don't duplicate that work
const generatedFor = new Set<string>();
return {
name: 'forward-file-imports',
async generateBundle(outputOptions, bundle, isWrite) {
if (!isWrite) {
return;
}
const dir = outputOptions.dir || dirname(outputOptions.file!);
if (generatedFor.has(dir)) {
return;
}
for (const output of Object.values(bundle)) {
if (output.type !== 'chunk') {
continue;
}
const chunk = output as OutputChunk;
// This'll be an absolute path pointing to the initial index file of the
// build, and we use it to find the location of the `src` dir
if (!chunk.facadeModuleId) {
continue;
}
generatedFor.add(dir);
// We're assuming that the index file is at the root of the source dir, and
// that all assets exist within that dir.
const srcRoot = dirname(chunk.facadeModuleId);
// Copy all the files we found into the dist dir
await Promise.all(
Array.from(exportedFiles).map(async exportedFile => {
const outputPath = relativePath(srcRoot, exportedFile);
const targetFile = resolvePath(dir, outputPath);
await fs.ensureDir(dirname(targetFile));
await fs.copyFile(exportedFile, targetFile);
}),
);
return;
}
},
options(inputOptions) {
const origExternal = inputOptions.external;
// We decorate any existing `external` option with our own way of determining
// if a module should be external. The can't use `resolveId`, since asset files
// aren't passed there, might be some better way to do this though.
const external: InputOptions['external'] = (id, importer, isResolved) => {
// Call to inner external option
if (
typeof origExternal === 'function' &&
origExternal(id, importer, isResolved)
) {
return true;
}
if (Array.isArray(origExternal) && origExternal.includes(id)) {
return true;
}
// The piece that we're adding
if (!filter(id)) {
return false;
}
// Sanity check, dunno if this can happen
if (!importer) {
throw new Error(`Unknown importer of file module ${id}`);
}
// Resolve relative imports to the full file URL, for deduping and copying later
const fullId = isResolved ? id : resolvePath(dirname(importer), id);
exportedFiles.add(fullId);
// Treating this module as external from here, meaning rollup won't try to
// put it in the output bundle, but still keep track of the relative imports
// as needed in the output code.
return true;
};
return { ...inputOptions, external };
},
};
}
Example #5
Source File: rollupAdapter.ts From web with MIT License | 4 votes |
export function rollupAdapter(
rollupPlugin: RollupPlugin,
rollupInputOptions: Partial<InputOptions> = {},
adapterOptions: RollupAdapterOptions = {},
): WdsPlugin {
if (typeof rollupPlugin !== 'object') {
throw new Error('rollupAdapter should be called with a rollup plugin object.');
}
const transformedFiles = new Set();
const pluginMetaPerModule = new Map<string, CustomPluginOptions>();
let rollupPluginContexts: RollupPluginContexts;
let fileWatcher: FSWatcher;
let config: DevServerCoreConfig;
let rootDir: string;
function savePluginMeta(
id: string,
{ meta }: { meta?: CustomPluginOptions | null | undefined } = {},
) {
if (!meta) {
return;
}
const previousMeta = pluginMetaPerModule.get(id);
pluginMetaPerModule.set(id, { ...previousMeta, ...meta });
}
const wdsPlugin: WdsPlugin = {
name: rollupPlugin.name,
async serverStart(args) {
({ fileWatcher, config } = args);
({ rootDir } = config);
rollupPluginContexts = await createRollupPluginContexts(rollupInputOptions);
// call the options and buildStart hooks
rollupPlugin.options?.call(rollupPluginContexts.minimalPluginContext, rollupInputOptions) ??
rollupInputOptions;
rollupPlugin.buildStart?.call(
rollupPluginContexts.pluginContext,
rollupPluginContexts.normalizedInputOptions,
);
},
async resolveImport({ source, context, code, column, line }) {
if (context.response.is('html') && source.startsWith('�')) {
// when serving HTML a null byte gets parsed as an unknown character
// we remap it to a null byte here so that it is handled properly downstream
// this isn't a perfect solution
source = source.replace('�', '\0');
}
// if we just transformed this file and the import is an absolute file path
// we need to rewrite it to a browser path
const injectedFilePath = path.normalize(source).startsWith(rootDir);
if (!injectedFilePath && !rollupPlugin.resolveId) {
return;
}
if (!injectedFilePath && !path.isAbsolute(source) && whatwgUrl.parseURL(source) != null) {
// don't resolve relative and valid urls
return source;
}
const filePath = getRequestFilePath(context.url, rootDir);
try {
const rollupPluginContext = createRollupPluginContextAdapter(
rollupPluginContexts.pluginContext,
wdsPlugin,
config,
fileWatcher,
context,
pluginMetaPerModule,
);
let resolvableImport = source;
let importSuffix = '';
// we have to special case node-resolve because it doesn't support resolving
// with hash/params at the moment
if (rollupPlugin.name === 'node-resolve') {
if (source[0] === '#') {
// private import
resolvableImport = source;
} else {
const [withoutHash, hash] = source.split('#');
const [importPath, params] = withoutHash.split('?');
importSuffix = `${params ? `?${params}` : ''}${hash ? `#${hash}` : ''}`;
resolvableImport = importPath;
}
}
let result = await rollupPlugin.resolveId?.call(
rollupPluginContext,
resolvableImport,
filePath,
{ isEntry: false },
);
if (!result && injectedFilePath) {
// the import is a file path but it was not resolved by this plugin
// we do assign it here so that it will be converted to a browser path
result = resolvableImport;
}
let resolvedImportPath: string | undefined = undefined;
if (typeof result === 'string') {
resolvedImportPath = result;
} else if (typeof result === 'object' && typeof result?.id === 'string') {
resolvedImportPath = result.id;
savePluginMeta(result.id, result);
}
if (!resolvedImportPath) {
if (
!['/', './', '../'].some(prefix => resolvableImport.startsWith(prefix)) &&
adapterOptions.throwOnUnresolvedImport
) {
const errorMessage = red(`Could not resolve import ${cyan(`"${source}"`)}.`);
if (
typeof code === 'string' &&
typeof column === 'number' &&
typeof line === 'number'
) {
throw new PluginSyntaxError(errorMessage, filePath, code, column, line);
} else {
throw new PluginError(errorMessage);
}
}
return undefined;
}
// if the resolved import includes a null byte (\0) there is some special logic
// these often are not valid file paths, so the browser cannot request them.
// we rewrite them to a special URL which we deconstruct later when we load the file
if (resolvedImportPath.includes('\0')) {
const filename = path.basename(
resolvedImportPath.replace(/\0*/g, '').split('?')[0].split('#')[0],
);
// if the resolve import path is outside our normal root, fully resolve the file path for rollup
const matches = resolvedImportPath.match(OUTSIDE_ROOT_REGEXP);
if (matches) {
const upDirs = new Array(parseInt(matches[1], 10) + 1).join(`..${path.sep}`);
resolvedImportPath = `\0${path.resolve(`${upDirs}${matches[2]}`)}`;
}
const urlParam = encodeURIComponent(resolvedImportPath);
return `${VIRTUAL_FILE_PREFIX}/${filename}?${NULL_BYTE_PARAM}=${urlParam}`;
}
// some plugins don't return a file path, so we just return it as is
if (!isAbsoluteFilePath(resolvedImportPath)) {
return `${resolvedImportPath}`;
}
// file already resolved outsided root dir
if (isOutsideRootDir(resolvedImportPath)) {
return `${resolvedImportPath}`;
}
const normalizedPath = path.normalize(resolvedImportPath);
// append a path separator to rootDir so we are actually testing containment
// of the normalized path within the rootDir folder
const checkRootDir = rootDir.endsWith(path.sep) ? rootDir : rootDir + path.sep;
if (!normalizedPath.startsWith(checkRootDir)) {
const relativePath = path.relative(rootDir, normalizedPath);
const dirUp = `..${path.sep}`;
const lastDirUpIndex = relativePath.lastIndexOf(dirUp) + 3;
const dirUpStrings = relativePath.substring(0, lastDirUpIndex).split(path.sep);
if (dirUpStrings.length === 0 || dirUpStrings.some(str => !['..', ''].includes(str))) {
// we expect the relative part to consist of only ../ or ..\\
const errorMessage =
red(`\n\nResolved ${cyan(source)} to ${cyan(resolvedImportPath)}.\n\n`) +
red(
'This path could not be converted to a browser path. Please file an issue with a reproduction.',
);
if (
typeof code === 'string' &&
typeof column === 'number' &&
typeof line === 'number'
) {
throw new PluginSyntaxError(errorMessage, filePath, code, column, line);
} else {
throw new PluginError(errorMessage);
}
}
const importPath = toBrowserPath(relativePath.substring(lastDirUpIndex));
resolvedImportPath = `/__wds-outside-root__/${dirUpStrings.length - 1}/${importPath}`;
} else {
const resolveRelativeTo = path.extname(filePath) ? path.dirname(filePath) : filePath;
const relativeImportFilePath = path.relative(resolveRelativeTo, resolvedImportPath);
resolvedImportPath = `./${toBrowserPath(relativeImportFilePath)}`;
}
return `${resolvedImportPath}${importSuffix}`;
} catch (error) {
throw wrapRollupError(filePath, context, error);
}
},
async serve(context) {
if (!rollupPlugin.load) {
return;
}
if (
context.path.startsWith(WDS_FILE_PREFIX) &&
!context.path.startsWith(VIRTUAL_FILE_PREFIX)
) {
return;
}
let filePath;
if (
context.path.startsWith(VIRTUAL_FILE_PREFIX) &&
context.URL.searchParams.has(NULL_BYTE_PARAM)
) {
// if this was a special URL constructed in resolveImport to handle null bytes,
// the file path is stored in the search paramter
filePath = context.URL.searchParams.get(NULL_BYTE_PARAM) as string;
} else {
filePath = path.join(rootDir, context.path);
}
try {
const rollupPluginContext = createRollupPluginContextAdapter(
rollupPluginContexts.pluginContext,
wdsPlugin,
config,
fileWatcher,
context,
pluginMetaPerModule,
);
const result = await rollupPlugin.load?.call(rollupPluginContext, filePath);
if (typeof result === 'string') {
return { body: result, type: 'js' };
}
if (typeof result?.code === 'string') {
savePluginMeta(filePath, result);
return { body: result.code, type: 'js' };
}
} catch (error) {
throw wrapRollupError(filePath, context, error);
}
return undefined;
},
async transform(context) {
if (!rollupPlugin.transform) {
return;
}
if (context.path.startsWith(WDS_FILE_PREFIX)) {
return;
}
if (context.response.is('js')) {
const filePath = path.join(rootDir, context.path);
try {
const rollupPluginContext = createRollupPluginContextAdapter(
rollupPluginContexts.transformPluginContext,
wdsPlugin,
config,
fileWatcher,
context,
pluginMetaPerModule,
);
const result = await rollupPlugin.transform?.call(
rollupPluginContext as TransformPluginContext,
context.body as string,
filePath,
);
let transformedCode: string | undefined = undefined;
if (typeof result === 'string') {
transformedCode = result;
}
if (typeof result === 'object' && typeof result?.code === 'string') {
savePluginMeta(filePath, result);
transformedCode = result.code;
}
if (transformedCode) {
transformedFiles.add(context.path);
return transformedCode;
}
return;
} catch (error) {
throw wrapRollupError(filePath, context, error);
}
}
if (context.response.is('html')) {
const documentAst = parseHtml(context.body as string);
const inlineScripts = queryAll(
documentAst,
predicates.AND(
predicates.hasTagName('script'),
predicates.NOT(predicates.hasAttr('src')),
),
);
const filePath = getRequestFilePath(context.url, rootDir);
let transformed = false;
try {
for (const node of inlineScripts) {
const code = getTextContent(node);
const rollupPluginContext = createRollupPluginContextAdapter(
rollupPluginContexts.transformPluginContext,
wdsPlugin,
config,
fileWatcher,
context,
pluginMetaPerModule,
);
const result = await rollupPlugin.transform?.call(
rollupPluginContext as TransformPluginContext,
code,
filePath,
);
let transformedCode: string | undefined = undefined;
if (typeof result === 'string') {
transformedCode = result;
}
if (typeof result === 'object' && typeof result?.code === 'string') {
savePluginMeta(filePath, result);
transformedCode = result.code;
}
if (transformedCode) {
transformedFiles.add(context.path);
setTextContent(node, transformedCode);
transformed = true;
}
}
if (transformed) {
return serializeHtml(documentAst);
}
} catch (error) {
throw wrapRollupError(filePath, context, error);
}
}
},
fileParsed(context) {
if (!rollupPlugin.moduleParsed) {
return;
}
const rollupPluginContext = createRollupPluginContextAdapter(
rollupPluginContexts.transformPluginContext,
wdsPlugin,
config,
fileWatcher,
context,
pluginMetaPerModule,
);
const filePath = getRequestFilePath(context.url, rootDir);
const info = rollupPluginContext.getModuleInfo(filePath);
if (!info) throw new Error(`Missing info for module ${filePath}`);
rollupPlugin.moduleParsed?.call(rollupPluginContext as TransformPluginContext, info);
},
};
return wdsPlugin;
}