rollup#RollupOptions TypeScript Examples

The following examples show how to use rollup#RollupOptions. 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: packager.ts    From backstage with Apache License 2.0 6 votes vote down vote up
async function rollupBuild(config: RollupOptions) {
  try {
    const bundle = await rollup(config);
    if (config.output) {
      for (const output of [config.output].flat()) {
        await bundle.generate(output);
        await bundle.write(output);
      }
    }
  } catch (error) {
    throw new Error(formatErrorMessage(error));
  }
}
Example #2
Source File: rollupBundlePlugin.ts    From web with MIT License 6 votes vote down vote up
async function bundleEntrypoints(rollupConfig: RollupOptions) {
  const bundle = await rollup(rollupConfig);
  if (Array.isArray(rollupConfig.output)) {
    throw new Error('Multiple outputs not supported.');
  }

  return bundle.generate({
    ...rollupConfig.output,
    chunkFileNames: '__rollup-generated__[name].js',
    assetFileNames: '__rollup-generated__[name][extname]',
  });
}
Example #3
Source File: buildAndWrite.ts    From web with MIT License 6 votes vote down vote up
export async function buildAndWrite(options: RollupOptions) {
  const bundle = await rollup(options);

  if (Array.isArray(options.output)) {
    await bundle.write(options.output[0]);
    await bundle.write(options.output[1]);
  } else if (options.output) {
    await bundle.write(options.output);
  }
}
Example #4
Source File: gulpfile.ts    From gdir with Do What The F*ck You Want To Public License 6 votes vote down vote up
gulp.task('app.rollup', async () => {
    const input: RollupOptions = {
        input: './app/index.tsx',
        external: ['react', 'react-dom', 'react-router-dom', 'rxjs', 'rxjs/ajax', 'rxjs/operators'],
        plugins: [
            resolve({
                extensions: ['.js', '.ts', '.tsx'],
            }),
            commonjs({
                include: 'node_modules/**',
                extensions: ['.js', '.ts'],
            }),
            typescriptPlugin({
                tsconfig: './app/tsconfig.json',
            }),
        ],
    };
    const output: RollupOptions = {
        output: {
            file: './dist/static/app.js',
            format: 'iife',
            sourcemap: 'inline',
            globals: {
                react: 'React',
                'react-dom': 'ReactDOM',
                'react-router-dom': 'ReactRouterDOM',
                rxjs: 'rxjs',
                'rxjs/ajax': 'rxjs.ajax',
                'rxjs/operators': 'rxjs.operators',
            },
        },
    };

    const bundle = await rollup(input);
    await bundle.write(output);
});
Example #5
Source File: gulpfile.ts    From gdir with Do What The F*ck You Want To Public License 6 votes vote down vote up
gulp.task('worker.rollup', async () => {
    const output: OutputOptions = {
        file: './dist/worker.js',
        format: 'iife',
        sourcemap: true,
    };
    const input: RollupOptions = {
        input: './worker/index.ts',
        plugins: [
            resolve({
                extensions: ['.js', '.ts'],
            }),
            commonjs({
                include: 'node_modules/**',
                extensions: ['.js', '.ts'],
            }),
            typescriptPlugin({
                tsconfig: './worker/tsconfig.json',
            }),
        ],
    };
    const bundle = await rollup(input);
    await bundle.write(output);
});
Example #6
Source File: compile.ts    From mantine with MIT License 5 votes vote down vote up
export default async function compile(config: RollupOptions) {
  const build = await rollup(config);
  const outputs: OutputOptions[] = Array.isArray(config.output) ? config.output : [config.output];

  return Promise.all(outputs.map((output) => build.write(output)));
}
Example #7
Source File: rollup.config.ts    From gitmars with GNU General Public License v3.0 5 votes vote down vote up
options: RollupOptions[] = []
Example #8
Source File: rollup.ts    From Cromwell with MIT License 4 votes vote down vote up
rollupConfigWrapper = async (moduleInfo: TPackageCromwellConfig, moduleConfig?: TModuleConfig, watch?: boolean): Promise<RollupOptions[]> => {

    if (!moduleInfo) throw new Error(`CromwellPlugin Error. Provide config as second argument to the wrapper function`);
    if (!moduleInfo?.type) throw new Error(`CromwellPlugin Error. Provide one of types to the CromwellConfig: 'plugin', 'theme'`);

    try {
        const pckg: TPackageJson = require(resolve(process.cwd(), 'package.json'));
        if (pckg?.name) moduleInfo.name = pckg.name;
    } catch (e) {
        logger.error('Failed to find package.json in project root');
        logger.error(e);
    }

    if (!moduleInfo.name) throw new Error(`CromwellPlugin Error. Failed to find name of the package in working directory`);

    const strippedName = moduleInfo.name.replace(/\W/g, '_');


    const packagePaths = await globPackages(process.cwd());
    const packages = collectPackagesInfo(packagePaths);
    const frontendDeps = await collectFrontendDependencies(packages, false);

    let specifiedOptions: TRollupConfig | undefined = moduleConfig?.rollupConfig?.() as any;
    if (isPromise(specifiedOptions)) specifiedOptions = await specifiedOptions as any;

    const inputOptions = specifiedOptions?.main;
    const outOptions: RollupOptions[] = [];

    const resolveFileExtension = (basePath: string): string | undefined => {
        const globStr = `${normalizePath(resolve(process.cwd(), basePath))}.+(ts|tsx|js|jsx)`;
        const files = glob.sync(globStr);
        return files[0]
    }


    if (moduleInfo.type === 'plugin') {
        const pluginConfig = moduleConfig as TPluginConfig | undefined;

        let frontendInputFile = pluginConfig?.frontendInputFile;
        if (!frontendInputFile) {
            frontendInputFile = resolveFileExtension('src/frontend/index');
        }

        if (frontendInputFile) {
            // Plugin frontend

            const options: RollupOptions = (Object.assign({}, specifiedOptions?.frontend ?? inputOptions));
            const inputPath = isAbsolute(frontendInputFile) ? normalizePath(frontendInputFile) :
                normalizePath(resolve(process.cwd(), frontendInputFile));

            const optionsInput = '$$' + moduleInfo.name + '/' + frontendInputFile;
            options.input = optionsInput;
            options.output = Object.assign({}, options.output, {
                file: resolve(process.cwd(), buildDirName, pluginFrontendBundlePath),
                format: "iife",
                exports: 'default',
                name: strippedName,
                banner: '(function() {',
                footer: `return ${strippedName};})();`
            } as OutputOptions);

            options.plugins = [...(options.plugins ?? [])];

            options.plugins.push(virtual({
                [optionsInput]: `
                        import defaultComp from '${inputPath}';
                        export default defaultComp;
                        `
            }))
            options.plugins.push(await rollupPluginCromwellFrontend({
                moduleInfo,
                moduleConfig,
                frontendDeps,
            }));
            outOptions.push(options);

            // Plugin frontend cjs (for getStaticPaths at server)
            const cjsOptions = Object.assign({}, specifiedOptions?.frontendCjs ?? inputOptions);

            cjsOptions.input = optionsInput;
            cjsOptions.output = Object.assign({}, cjsOptions.output, {
                file: resolve(process.cwd(), buildDirName, pluginFrontendCjsPath),
                format: "cjs",
                name: moduleInfo.name,
                exports: "auto"
            } as OutputOptions)

            cjsOptions.plugins = [...(cjsOptions.plugins ?? [])];

            cjsOptions.plugins.push(virtual({
                [optionsInput]: `
                    import * as allExports from '${inputPath}';
                    export default allExports;
                    `
            }))
            cjsOptions.plugins.push(await rollupPluginCromwellFrontend({
                generateMeta: false,
                moduleInfo,
                moduleConfig,
            }));
            outOptions.push(cjsOptions);

        }

        // Plugin admin panel
        let adminInputFile = pluginConfig?.adminInputFile;
        if (!adminInputFile) {
            adminInputFile = resolveFileExtension('src/admin/index');
        }

        if (adminInputFile) {
            const options = (Object.assign({}, specifiedOptions?.frontend ?? inputOptions));
            const inputPath = isAbsolute(adminInputFile) ? normalizePath(adminInputFile) :
                normalizePath(resolve(process.cwd(), adminInputFile));

            const optionsInput = '$$' + moduleInfo.name + '/' + adminInputFile;
            options.input = optionsInput;
            options.output = Object.assign({}, options.output, {
                file: resolve(process.cwd(), buildDirName, pluginAdminBundlePath),
                format: "iife",
                name: strippedName,
            } as OutputOptions);

            options.plugins = [...(options.plugins ?? [])];

            options.plugins.push(virtual({
                [optionsInput]: `
                        import '${inputPath}';
                        `
            }))
            options.plugins.push(await rollupPluginCromwellFrontend({
                moduleInfo,
                moduleConfig,
                frontendDeps,
            }));
            outOptions.push(options);
        }

        // Plugin backend
        const backendInputFile = resolveFileExtension(pluginConfig?.backend ?? 'src/backend/index');
        if (backendInputFile) {
            const cjsOptions = Object.assign({}, specifiedOptions?.backend ?? inputOptions);

            cjsOptions.input = backendInputFile;
            cjsOptions.output = Object.assign({}, cjsOptions.output, {
                file: getPluginBackendPath(resolve(process.cwd(), buildDirName)),
                format: "cjs",
                name: moduleInfo.name,
                exports: "auto"
            } as OutputOptions)

            cjsOptions.plugins = [...(cjsOptions.plugins ?? [])];

            cjsOptions.external = isExternalForm;

            outOptions.push(cjsOptions);
        }

        // Copy static into public
        if (watch) {
            startStaticWatcher(moduleInfo.name, 'plugin');
        } else {
            const pluginStaticDir = await getModuleStaticDir(moduleInfo.name)
            if (pluginStaticDir && await fs.pathExists(pluginStaticDir)) {
                try {
                    const publicPluginsDir = getPublicPluginsDir();
                    await fs.ensureDir(publicPluginsDir);
                    await fs.copy(pluginStaticDir, resolve(publicPluginsDir, moduleInfo.name));
                } catch (e) { logger.log(e) }
            }
        }
    }

    if (moduleInfo.type === 'theme') {
        const buildDir = normalizePath(getThemeTempRollupBuildDir());
        if (!buildDir) throw new Error(`CromwellPlugin Error. Failed to find package directory of ` + moduleInfo.name);

        let srcDir = process.cwd();
        let pagesDirChunk = 'pages';
        let pagesDir = resolve(srcDir, pagesDirChunk);

        if (!fs.existsSync(pagesDir)) {
            srcDir = resolve(process.cwd(), 'src');
            pagesDir = resolve(srcDir, 'pages');
            pagesDirChunk = 'src/pages';
        }

        if (!fs.pathExistsSync(pagesDir)) {
            throw new Error('Pages directory was not found')
        }

        // Theme frontend
        const options: RollupOptions = (Object.assign({}, specifiedOptions?.themePages ? specifiedOptions.themePages : inputOptions));
        const globStr = `${pagesDir}/**/*.+(ts|tsx|js|jsx)`;
        const pageFiles = glob.sync(globStr);

        const pagesMetaInfo = await collectPages({
            buildDir,
            pagesDir,
        });

        const dependencyOptions: RollupOptions[] = [];

        if (pageFiles && pageFiles.length > 0) {
            options.plugins = [...(options.plugins ?? [])];

            options.input = getThemePagesVirtualPath(buildDir);

            options.output = Object.assign({}, options.output, {
                dir: buildDir,
                format: "esm",
                preserveModules: true
            } as OutputOptions);

            options.plugins.push(scssExternalPlugin());

            if (!options.plugins.find(plugin => typeof plugin === 'object' && plugin?.name === '@rollup/plugin-babel' ||
                typeof plugin === 'object' && plugin?.name === 'babel'))
                options.plugins.push(babel({
                    extensions: ['.js', '.jsx', '.ts', '.tsx'],
                    babelHelpers: 'bundled',
                    presets: ['@babel/preset-react']
                }));

            options.plugins.push(await rollupPluginCromwellFrontend({
                pagesMetaInfo, buildDir, srcDir, moduleInfo,
                moduleConfig, watch,
                generatePagesMeta: true,
                frontendDeps, dependencyOptions,
                type: 'themePages',
                pagesDir,
            }));

            if (!options.plugins.find(plugin => typeof plugin === 'object' && plugin?.name === '@rollup/plugin-node-resolve'
                || typeof plugin === 'object' && plugin?.name === 'node-resolve'))
                options.plugins.push(nodeResolve({
                    extensions: ['.js', '.jsx', '.ts', '.tsx'],
                }));

            outOptions.push(options);

            if (watch) {
                startPagesWatcher(buildDir, pagesDir);
            }
        } else {
            throw new Error('CromwellPlugin Error. No pages found at: ' + pagesDir);
        }


        // Copy static into public
        if (watch) {
            startStaticWatcher(moduleInfo.name, 'theme');
        } else {
            const themeStaticDir = await getModuleStaticDir(moduleInfo.name);
            if (themeStaticDir && await fs.pathExists(themeStaticDir)) {
                try {
                    const publicThemesDir = getPublicThemesDir();
                    await fs.ensureDir(publicThemesDir);
                    await fs.copy(themeStaticDir, resolve(publicThemesDir, moduleInfo.name), {
                        overwrite: false,
                    });
                } catch (e) { logger.log(e) }
            }
        }


        // Theme admin panel page-controller 
        const adminPanelDirChunk = 'admin-panel';
        const adminPanelDir = resolve(srcDir, adminPanelDirChunk);
        if (!watch && fs.pathExistsSync(adminPanelDir)) {
            const adminOptions: RollupOptions = (Object.assign({}, specifiedOptions?.themePages ? specifiedOptions.themePages : inputOptions));

            adminOptions.plugins = [...(adminOptions.plugins ?? [])];

            const optionsInput = '$$' + moduleInfo.name + '/' + adminPanelDirChunk;
            adminOptions.plugins.push(virtual({
                [optionsInput]: `import settings from '${normalizePath(adminPanelDir)}'; export default settings`
            }));
            adminOptions.input = optionsInput;

            adminOptions.output = Object.assign({}, adminOptions.output, {
                dir: resolve(buildDir, 'admin', '_settings'),
                format: "iife",
            } as OutputOptions);

            adminOptions.plugins.push(await rollupPluginCromwellFrontend({
                pagesMetaInfo, buildDir, srcDir,
                moduleInfo, moduleConfig, frontendDeps,
                pagesDir,
            }));

            outOptions.push(adminOptions);
        }
    }

    return outOptions;
}
Example #9
Source File: rollup.ts    From Cromwell with MIT License 4 votes vote down vote up
rollupPluginCromwellFrontend = async (settings?: {
    packageJsonPath?: string;
    generateMeta?: boolean;
    generatePagesMeta?: boolean;
    pagesMetaInfo?: TPagesMetaInfo;
    buildDir?: string;
    srcDir?: string;
    pagesDir?: string;
    watch?: boolean;
    moduleInfo?: TPackageCromwellConfig;
    moduleConfig?: TModuleConfig;
    frontendDeps?: TFrontendDependency[];
    dependencyOptions?: RollupOptions[];
    type?: 'themePages' | 'themeAdminPanel';
}): Promise<Plugin> => {

    const scriptsInfo: Record<string, TScriptMetaInfo> = {};
    const importsInfo: Record<string, {
        externals: Record<string, string[]>;
        internals: string[];
    }> = {};

    if (settings?.frontendDeps) settings.frontendDeps = await parseFrontendDeps(settings.frontendDeps);

    // Defer stylesheetsLoader until compile info available. Look for: stylesheetsLoaderStarter()
    let stylesheetsLoaderStarter;
    if (settings?.srcDir && settings?.buildDir) {
        (async () => {
            await new Promise(resolve => {
                stylesheetsLoaderStarter = resolve;
            })
            if (settings?.srcDir && settings?.buildDir)
                initStylesheetsLoader(settings?.srcDir, settings?.buildDir, settings?.pagesMetaInfo?.basePath, settings?.watch);
        })();

    }

    const packageJson: TPackageJson = require(resolve(process.cwd(), 'package.json'));

    const plugin: Plugin = {
        name: 'cromwell-frontend',
        options(options: RollupOptions) {
            if (!options.plugins) options.plugins = [];

            options.plugins.push(externalGlobals((id: string) => {
                if (settings?.moduleInfo?.type === 'theme' && settings?.pagesMetaInfo?.paths) {
                    // Disable for Theme pages
                    return;
                }

                const extStr = `${cromwellStoreModulesPath}["${id}"]`;
                if (id.startsWith('next/')) {
                    return extStr;
                }
                const isExt = resolveExternal(id, settings?.frontendDeps);
                if (isExt) return extStr;
            }, {
                include: '**/*.+(ts|tsx|js|jsx)',
                createVars: true,
            }));

            return options;
        },
        resolveId(source) {
            if (settings?.moduleInfo?.type === 'theme' && settings?.pagesMetaInfo?.paths) {
                // If bundle frontend pages mark css as external to leave it to Next.js Webpack
                if (/\.s?css$/.test(source)) {
                    return { id: source, external: true };
                }

                // All node_modules are external to leave them to next.js
                if (isExternalForm(source)) {
                    return { id: source, external: true };
                }
            }

            if (resolveExternal(source, settings?.frontendDeps)) {
                // Mark external frontendDependencies from package.json
                return { id: source, external: true };
            }

            if (isExternalForm(source)) {
                // Other node_modules...

                if (source.startsWith('next/')) {
                    // Leave Next.js modules external for the next step 
                    return { id: source, external: true };
                }

                if (/\.s?css$/.test(source)) {
                    // Bundle css with AdminPanel pages
                    return { id: require.resolve(source), external: false };
                }

                // Bundled by Rollup by for Themes and Plugins
                return { id: require.resolve(source), external: false };
            }

            return null;
        },
    };

    if (settings?.generateMeta !== false) {
        plugin.transform = function (code, id): string | null {
            id = normalizePath(id).replace('\x00', '');
            if (!/\.(m?jsx?|tsx?)$/.test(id)) return null;
            const ast = this.parse(code);
            
            walk(ast, {
                enter(node: any) {
                    if (node.type === 'ImportDeclaration') {
                        if (!node.specifiers || !node.source) return;
                        const source = node.source.value.replace('\x00', '');

                        if (!importsInfo[id]) importsInfo[id] = {
                            externals: {},
                            internals: []
                        };

                        if (!isExternalForm(source)) {
                            const absolutePath = resolve(dirname(id), source);
                            const globStr = `${absolutePath}.+(ts|tsx|js|jsx)`;
                            const targetFiles = glob.sync(globStr);
                            if (targetFiles[0]) importsInfo[id].internals.push(targetFiles[0]);
                            return;
                        }

                        // Add external
                        if (resolveExternal(source, settings?.frontendDeps)) {
                            if (!importsInfo[id].externals[source]) importsInfo[id].externals[source] = [];

                            node.specifiers.forEach(spec => {
                                if (spec.type === 'ImportDefaultSpecifier' || spec.type === 'ImportNamespaceSpecifier') {
                                    importsInfo[id].externals[source].push('default')
                                }
                                if (spec.type === 'ImportSpecifier' && spec.imported) {
                                    importsInfo[id].externals[source].push(spec.imported.name)
                                }
                            })
                        }
                    }
                }
            });

            return null;
        };

        plugin.generateBundle = async function (options, bundle) {

            if (settings?.pagesMetaInfo && settings.buildDir && settings.pagesDir) {
                settings.pagesMetaInfo = await collectPages({
                    buildDir: settings.buildDir,
                    pagesDir: settings.pagesDir,
                });
            }

            Object.values(bundle).forEach((info: any) => {

                const mergeBindings = (bindings1, bindings2): Record<string, string[]> => {
                    const mergedBindings = Object.assign({}, bindings1);

                    Object.keys(bindings2).forEach(modDep => {
                        if (mergedBindings[modDep]) {
                            mergedBindings[modDep] = [...mergedBindings[modDep], ...bindings2[modDep]];
                            mergedBindings[modDep] = Array.from(new Set(mergedBindings[modDep]));
                        }
                        else mergedBindings[modDep] = bindings2[modDep];
                    })

                    return mergedBindings;
                }

                const importBindingsCache: Record<string, Record<string, string[]>> = {}
                const getImportBingingsForModule = (modId: string): Record<string, string[]> => {
                    modId = normalizePath(modId);
                    if (importBindingsCache[modId]) return importBindingsCache[modId];

                    let importedBindings = {};
                    importBindingsCache[modId] = {};
                    if (importsInfo[modId]) {
                        Object.keys(importsInfo[modId].externals).forEach(libName => {
                            if (!isExternalForm(libName)) return;

                            const importsSpecs = importsInfo[modId].externals[libName];
                            importsSpecs?.forEach(spec => {
                                if (!importedBindings[libName]) importedBindings[libName] = [];
                                if (!importedBindings[libName].includes(spec)) {
                                    importedBindings[libName].push(spec);
                                }
                            });
                        });

                        importsInfo[modId].internals.forEach(internal => {
                            const internalBinds = getImportBingingsForModule(internal);
                            importedBindings = mergeBindings(importedBindings, internalBinds);
                        })
                    }
                    importBindingsCache[modId] = importedBindings;
                    return importedBindings;
                }

                let totalImportedBindings = {};

                if (info.modules) {
                    Object.keys(info.modules).forEach(modId => {
                        const modBindings = getImportBingingsForModule(modId);
                        totalImportedBindings = mergeBindings(totalImportedBindings, modBindings);
                    })
                }

                const versionedImportedBindings = {};
                Object.keys(totalImportedBindings).forEach(dep => {
                    let ver = getNodeModuleVersion(dep, process.cwd());
                    if (!ver) {
                        ver = getDepVersion(packageJson, dep);
                    }
                    if (ver) {
                        if (totalImportedBindings[dep].includes('default')) totalImportedBindings[dep] = ['default'];
                        versionedImportedBindings[`${dep}@${ver}`] = totalImportedBindings[dep];
                    }
                })

                const metaInfo: TScriptMetaInfo = {
                    name: settings?.moduleInfo?.name + '/' + info.fileName + '_' + cryptoRandomString({ length: 8 }),
                    externalDependencies: versionedImportedBindings,
                    // importsInfo
                    // info
                };

                scriptsInfo[info.fileName] = metaInfo;

                this.emitFile({
                    type: 'asset',
                    fileName: getMetaInfoPath(info.fileName),
                    source: JSON.stringify(metaInfo, null, 2)
                });

                if (settings?.pagesMetaInfo?.paths && settings.buildDir && settings.generatePagesMeta) {
                    settings.pagesMetaInfo.paths = settings.pagesMetaInfo.paths.map(paths => {

                        if (info.fileName && paths.srcFullPath === normalizePath(info.facadeModuleId)) {
                            paths.localPath = info.fileName;

                            // Get base path chunk that appended in build dir relatively src dir.
                            // Since we use preserveModules: true, node_modules can appear in build dir.
                            // That will relatively move rollup's options.output.dir on a prefix 
                            // We don't know this prefix (basePath) before compile, so we calc it here: 
                            if (!settings?.pagesMetaInfo?.basePath && settings?.srcDir && info.facadeModuleId) {
                                let baseFileName: any = normalizePath(info.facadeModuleId).replace(normalizePath(settings.srcDir), '').split('.');
                                baseFileName.length > 1 ? baseFileName = baseFileName.slice(0, -1).join('.') : baseFileName.join('.');

                                let basePath: any = normalizePath(info.fileName).split('.');
                                basePath.length > 1 ? basePath = basePath.slice(0, -1).join('.') : basePath.join('.');
                                if ((basePath as string).startsWith('/')) basePath = (basePath as string).substring(1);
                                if ((baseFileName as string).startsWith('/')) baseFileName = (baseFileName as string).substring(1);

                                basePath = normalizePath(basePath).replace(baseFileName, '');
                                if (settings?.pagesMetaInfo) {
                                    settings.pagesMetaInfo.basePath = basePath;
                                    if (stylesheetsLoaderStarter) {
                                        stylesheetsLoaderStarter();
                                        stylesheetsLoaderStarter = null;
                                    }
                                }
                            }

                            delete paths.srcFullPath;
                        }

                        return paths;
                    })
                }
            });

            if (settings?.pagesMetaInfo?.paths && settings.srcDir && settings.buildDir && settings.generatePagesMeta) {
                await generatePagesMeta(settings.pagesMetaInfo, settings.buildDir)
            }
        }

        plugin.writeBundle = function () {
            if (hasToRunRendererGenerator) {
                hasToRunRendererGenerator = false;
                const { generator } = require('@cromwell/renderer/build/generator');
                generator({
                    scriptName: "build",
                    targetThemeName: settings?.moduleInfo?.name,
                });
            }
        }
    }

    return plugin;
}
Example #10
Source File: config.ts    From backstage with Apache License 2.0 4 votes vote down vote up
export async function makeRollupConfigs(
  options: BuildOptions,
): Promise<RollupOptions[]> {
  const configs = new Array<RollupOptions>();
  const targetDir = options.targetDir ?? paths.targetDir;
  const onwarn = ({ code, message }: RollupWarning) => {
    if (code === 'EMPTY_BUNDLE') {
      return; // We don't care about this one
    }
    if (options.logPrefix) {
      console.log(options.logPrefix + message);
    } else {
      console.log(message);
    }
  };

  const distDir = resolvePath(targetDir, 'dist');

  if (options.outputs.has(Output.cjs) || options.outputs.has(Output.esm)) {
    const output = new Array<OutputOptions>();
    const mainFields = ['module', 'main'];

    if (options.outputs.has(Output.cjs)) {
      output.push({
        dir: distDir,
        entryFileNames: 'index.cjs.js',
        chunkFileNames: 'cjs/[name]-[hash].cjs.js',
        format: 'commonjs',
        sourcemap: true,
      });
    }
    if (options.outputs.has(Output.esm)) {
      output.push({
        dir: distDir,
        entryFileNames: 'index.esm.js',
        chunkFileNames: 'esm/[name]-[hash].esm.js',
        format: 'module',
        sourcemap: true,
      });
      // Assume we're building for the browser if ESM output is included
      mainFields.unshift('browser');
    }

    configs.push({
      input: resolvePath(targetDir, 'src/index.ts'),
      output,
      onwarn,
      preserveEntrySignatures: 'strict',
      // All module imports are always marked as external
      external: (source, importer, isResolved) =>
        Boolean(importer && !isResolved && !isFileImport(source)),
      plugins: [
        resolve({ mainFields }),
        commonjs({
          include: /node_modules/,
          exclude: [/\/[^/]+\.(?:stories|test)\.[^/]+$/],
        }),
        postcss(),
        forwardFileImports({
          exclude: /\.icon\.svg$/,
          include: [
            /\.svg$/,
            /\.png$/,
            /\.gif$/,
            /\.jpg$/,
            /\.jpeg$/,
            /\.eot$/,
            /\.woff$/,
            /\.woff2$/,
            /\.ttf$/,
          ],
        }),
        json(),
        yaml(),
        svgr({
          include: /\.icon\.svg$/,
          template: svgrTemplate,
        }),
        esbuild({
          target: 'es2019',
          minify: options.minify,
        }),
      ],
    });
  }

  if (options.outputs.has(Output.types) && !options.useApiExtractor) {
    const typesInput = paths.resolveTargetRoot(
      'dist-types',
      relativePath(paths.targetRoot, targetDir),
      'src/index.d.ts',
    );

    const declarationsExist = await fs.pathExists(typesInput);
    if (!declarationsExist) {
      const path = relativePath(targetDir, typesInput);
      throw new Error(
        `No declaration files found at ${path}, be sure to run ${chalk.bgRed.white(
          'yarn tsc',
        )} to generate .d.ts files before packaging`,
      );
    }

    configs.push({
      input: typesInput,
      output: {
        file: resolvePath(distDir, 'index.d.ts'),
        format: 'es',
      },
      external: [
        /\.css$/,
        /\.scss$/,
        /\.sass$/,
        /\.svg$/,
        /\.eot$/,
        /\.woff$/,
        /\.woff2$/,
        /\.ttf$/,
      ],
      onwarn,
      plugins: [dts()],
    });
  }

  return configs;
}
Example #11
Source File: normalizeRollupConfig.ts    From icepkg with MIT License 4 votes vote down vote up
normalizeRollupConfig = (
  cfg: TaskConfig,
  ctx: PkgContext,
  taskName: TaskName,
): [RollupPlugin[], RollupOptions] => {
  const { swcCompileOptions, type, outputDir, rollupPlugins, rollupOptions } = cfg;
  const { rootDir, userConfig, pkg } = ctx;

  const commonPlugins = [
    userConfig?.alias && alias({ entries: userConfig.alias }),
    userConfig?.babelPlugins?.length && babelPlugin(userConfig.babelPlugins),
    swcPlugin({
      extraSwcOptions: swcCompileOptions,
    }),
  ].filter(Boolean);

  let resolvedPlugins = rollupPlugins ?? [];

  if (type === 'transform') {
    resolvedPlugins = [
      // dts plugin should append ahead for obtainig source code.
      // And dts plugin would never change the contents of file.
      dtsPlugin(cfg.entry, userConfig?.generateTypesForJs),
      ...resolvedPlugins,
      ...commonPlugins,
    ];

    return [resolvedPlugins, rollupOptions];
  }

  if (type === 'bundle') {
    resolvedPlugins = [
      ...resolvedPlugins,
      ...commonPlugins,
      postcss({
        plugins: [autoprefixer()],
        extract: resolve(rootDir, outputDir, 'index.css'),
        autoModules: true,
        minimize: true,
        sourceMap: userConfig?.sourceMaps,
      }),
      nodeResolve(), // To locates modules using the node resolution algorithm,
      commonjs(), // To convert commonjs to import, make it capabile for rollup to bundle
    ];

    const entry = isFile(cfg.entry) ? cfg.entry : findDefaultEntryFile(cfg.entry);

    if (!entry) {
      throw new Error(
        'Failed to resolve entry for current project.\n' +
        'This is most likely that \'src/index\' is not exist.\n' +
        'Whereas @ice/pkg treats it as the default option.',
      );
    }

    const [external, globals] = getExternalsAndGlboals(userConfig, pkg as PkgJson);

    const resolvedRollupOptions = deepmerge.all([
      {
        input: entry,
        external,
        globals,
        output: getRollupOutputs({
          userConfig,
          pkg: pkg as PkgJson,
          outputDir: cfg.outputDir,
          isES2017: taskName === TaskName.BUNDLE_ES2017,
        }),
      },
      cfg.rollupOptions || {},
      {
        plugins: [
          // Custom plugins will add ahead
          ...(cfg?.rollupOptions?.plugins || []),
          ...resolvedPlugins,
        ],
      },
    ]);

    return [resolvedPlugins, resolvedRollupOptions];
  }
}
Example #12
Source File: create-package-config.ts    From mantine with MIT License 4 votes vote down vote up
export default async function createPackageConfig(config: PkgConfigInput): Promise<RollupOptions> {
  const packageJson = JSON.parse(
    fs.readFileSync(path.join(config.basePath, './package.json')).toString('utf-8')
  );
  const pkgList = await getPackagesList();

  const aliasEntries: Alias[] = pkgList.map((pkg) => ({
    find: new RegExp(`^${pkg.packageJson.name}`),
    replacement: path.resolve(pkg.path, 'src'),
  }));

  const plugins = [
    commonjs(),
    nodeExternals(),
    nodeResolve({ extensions: ['.ts', '.tsx', '.js', '.jsx'] }),
    esbuild({
      minify: config.format === 'umd',
      sourceMap: false,
      tsconfig: path.resolve(process.cwd(), 'tsconfig.json'),
    }),
    json(),
    alias({ entries: aliasEntries }),
    replace({ preventAssignment: true }),
  ];

  let externals;

  if (config.format === 'umd') {
    externals = [
      ...(config?.externals || []),
      ...Object.keys({
        ...packageJson.peerDependencies,
      }),
    ];
  } else {
    externals = [
      '@emotion/server/create-instance',
      'dayjs/locale/ru',
      ...(config?.externals || []),
      ...Object.keys({
        ...packageJson.peerDependencies,
        ...packageJson.dependencies,
      }),
    ];
  }

  const output: OutputOptions = {
    name: packageJson.name,
    format: config.format as ModuleFormat,
    externalLiveBindings: false,
    sourcemap: config.sourcemap,
  };

  if (config.format === 'es') {
    output.dir = path.resolve(config.basePath, 'esm');
    output.preserveModules = true;
  }

  if (config.format === 'cjs') {
    output.dir = path.resolve(config.basePath, 'cjs');
    output.preserveModules = true;
    output.exports = 'named';
  }

  if (config.format === 'umd') {
    output.file = path.resolve(config.basePath, 'lib/index.umd.js');
    output.globals = {
      ...pkgList
        .map((pkg) => ({
          [pkg.packageJson.name]: pkg.packageJson.name,
        }))
        .reduce((globals, pkgGlobal) => ({ ...globals, ...pkgGlobal }), {}),
      react: 'React',
      dayjs: 'dayjs',
      'react-dom': 'ReactDOM',
    };
  }

  if (config.analyze && config.format === 'es') {
    plugins.push(
      visualizer({
        title: packageJson.name,
        filename: path.join(config.basePath, 'lib/stats.html'),
        projectRoot: path.join(config.basePath, 'src'),
        sourcemap: true,
        gzipSize: true,
      }),
      visualizer({
        title: packageJson.name,
        filename: path.join(config.basePath, 'lib/stats.json'),
        projectRoot: path.join(config.basePath, 'src'),
        json: true,
        sourcemap: true,
        gzipSize: true,
      })
    );
  }

  return {
    input: config?.entry || path.resolve(config.basePath, 'src/index.ts'),
    output,
    external: externals,
    plugins,
  };
}
Example #13
Source File: createRollupConfig.ts    From web with MIT License 4 votes vote down vote up
export function createRollupConfig(params: CreateRollupConfigParams): RollupOptions {
  const { outputDir, indexFilename, indexHtmlString, storyFilePaths } = params;

  const options: RollupOptions = {
    preserveEntrySignatures: false,
    onwarn,
    output: {
      entryFileNames: '[hash].js',
      chunkFileNames: '[hash].js',
      assetFileNames: '[hash][extname]',
      format: 'system',
      dir: outputDir,
    },
    plugins: [
      resolve({
        moduleDirectories: ['node_modules', 'web_modules'],
      }),
      babel({
        babelHelpers: 'bundled',
        babelrc: false,
        configFile: false,
        extensions: [...DEFAULT_EXTENSIONS, 'md', 'mdx'],
        exclude: `${prebuiltDir}/**`,
        presets: [
          [
            require.resolve('@babel/preset-env'),
            {
              targets: [
                'last 3 Chrome major versions',
                'last 3 ChromeAndroid major versions',
                'last 3 Firefox major versions',
                'last 3 Edge major versions',
                'last 3 Safari major versions',
                'last 3 iOS major versions',
                'ie 11',
              ],
              useBuiltIns: false,
              shippedProposals: true,
              modules: false,
              bugfixes: true,
            },
          ],
        ],
        plugins: [
          [require.resolve('babel-plugin-bundled-import-meta'), { importStyle: 'baseURI' }],
          [
            require.resolve('babel-plugin-template-html-minifier'),
            {
              modules: {
                // this is web component specific, but has no effect on other project styles
                'lit-html': ['html'],
                'lit-element': ['html', { name: 'css', encapsulation: 'style' }],
                '@web/storybook-prebuilt/web-components': [
                  'html',
                  { name: 'css', encapsulation: 'style' },
                ],
                '@web/storybook-prebuilt/web-components.js': [
                  'html',
                  { name: 'css', encapsulation: 'style' },
                ],
                '@open-wc/testing': ['html', { name: 'css', encapsulation: 'style' }],
                '@open-wc/testing-helpers': ['html', { name: 'css', encapsulation: 'style' }],
              },
              logOnError: true,
              failOnError: false,
              strictCSS: true,
              htmlMinifier: {
                collapseWhitespace: true,
                conservativeCollapse: true,
                removeComments: true,
                caseSensitive: true,
                minifyCSS: true,
              },
            },
          ],
        ],
      }) as Plugin,
      html({ input: { name: indexFilename, html: indexHtmlString } }) as Plugin,
      polyfillsLoader({
        polyfills: {
          coreJs: true,
          fetch: true,
          abortController: true,
          regeneratorRuntime: 'always',
          webcomponents: true,
          intersectionObserver: true,
          resizeObserver: true,
        },
      }) as Plugin,
      terser({ format: { comments: false } }) as Plugin,
    ],
  };

  if (storyFilePaths && storyFilePaths.length > 0) {
    // plugins we need to inject only in the preview
    options.plugins!.unshift(injectExportsOrderPlugin(storyFilePaths));
    options.plugins!.unshift(mdxPlugin());
    options.plugins!.unshift(mdjsPlugin(params.type));
  }

  return options;
}
Example #14
Source File: rollupPluginPolyfillsLoader.test.ts    From web with MIT License 4 votes vote down vote up
describe('rollup-plugin-polyfills-loader', function describe() {
  // bootup of the first test can take a long time in CI to load all the polyfills
  this.timeout(5000);

  it('can inject a polyfills loader with a single output', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `<script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>`,
          },
        }),
        polyfillsLoader({
          polyfills: { hash: false, fetch: true },
        }),
      ],
    };

    await testSnapshot({
      name: 'single-output',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('can inject a polyfills loader with multiple entrypoints', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-b.js"></script>`,
          },
        }),
        polyfillsLoader({
          polyfills: { hash: false, fetch: true },
        }),
      ],
    };

    await testSnapshot({
      name: 'multiple-entrypoints',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('retains attributes on script tags when injecting a polyfills loader with multiple entrypoints', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js" keep-this-attribute></script>
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-b.js"></script>`,
          },
        }),
        polyfillsLoader({
          polyfills: { hash: false, fetch: true },
        }),
      ],
    };

    await testSnapshot({
      name: 'multiple-entrypoints-retain-attributes',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('can inject a polyfills loader with non-flat inputs, flattenOutput: true', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          rootDir: `${relativeUrl}/fixtures/`,
          input: `non-flat/index.html`,
          flattenOutput: true,
        }),
        polyfillsLoader({
          polyfills: { hash: false, fetch: true },
        }),
      ],
    };

    await testSnapshot({
      name: 'flattened',
      fileName: `index.html`,
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('can inject a polyfills loader with non-flat inputs, flattenOutput: false', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          rootDir: `${relativeUrl}/fixtures/`,
          input: `non-flat/index.html`,
          flattenOutput: false,
        }),
        polyfillsLoader({
          polyfills: { hash: false, fetch: true },
        }),
      ],
    };

    await testSnapshot({
      name: 'non-flattened',
      fileName: path.normalize(`non-flat/index.html`),
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('injects the correct preload for systemjs output', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-b.js"></script>`,
          },
        }),
        polyfillsLoader(),
      ],
    };

    await testSnapshot({
      name: 'systemjs',
      fileName: 'index.html',
      inputOptions,
      outputOptions: [
        {
          format: 'system',
          dir: 'dist',
        },
      ],
    });
  });

  it('can set polyfills to load', async () => {
    const inputOptions = {
      plugins: [
        html({
          input: {
            html: `<script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>`,
          },
        }),
        polyfillsLoader({
          polyfills: {
            hash: false,
            webcomponents: true,
            fetch: true,
          },
        }),
      ],
    };

    const output = await testSnapshot({
      name: 'polyfills',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });

    expect(output.find(o => o.fileName.startsWith('polyfills/webcomponents'))).to.exist;
    expect(output.find(o => o.fileName.startsWith('polyfills/fetch'))).to.exist;
  });

  it('can inject with multiple build outputs', async () => {
    const htmlPlugin = html({
      input: {
        html: `<script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>`,
      },
    });
    const inputOptions = {
      plugins: [
        htmlPlugin,
        polyfillsLoader({
          modernOutput: { name: 'modern' },
          legacyOutput: [{ name: 'legacy', test: "!('noModule' in HTMLScriptElement.prototype)" }],
          polyfills: { hash: false, webcomponents: true, fetch: true },
        }),
      ],
    };

    const outputOptions: OutputOptions[] = [
      {
        format: 'system',
        dir: 'dist',
        plugins: [htmlPlugin.api.addOutput('legacy')],
      },
      {
        format: 'es',
        dir: 'dist',
        plugins: [htmlPlugin.api.addOutput('modern')],
      },
    ];

    await testSnapshot({
      name: 'multiple-outputs',
      fileName: 'index.html',
      inputOptions,
      outputOptions,
    });
  });

  it('can customize the file type', async () => {
    const htmlPlugin = html({
      input: {
        html: `<script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>`,
      },
    });
    const inputOptions = {
      plugins: [
        htmlPlugin,
        polyfillsLoader({
          modernOutput: { name: 'modern', type: 'systemjs' },
          polyfills: { hash: false, webcomponents: true, fetch: true },
        }),
      ],
    };

    const outputOptions: OutputOptions[] = [
      {
        format: 'es',
        dir: 'dist',
      },
    ];

    await testSnapshot({
      name: 'customize-filetype',
      fileName: 'index.html',
      inputOptions,
      outputOptions,
    });
  });

  it('can customize the file type for multiple outputs', async () => {
    const htmlPlugin = html({
      input: {
        html: `<script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>`,
      },
    });
    const inputOptions = {
      plugins: [
        htmlPlugin,
        polyfillsLoader({
          modernOutput: { name: 'modern', type: 'script' },
          legacyOutput: [
            {
              name: 'legacy',
              type: 'script',
              test: "!('noModule' in HTMLScriptElement.prototype)",
            },
          ],
          polyfills: { hash: false, webcomponents: true, fetch: true },
        }),
      ],
    };

    const outputOptions: OutputOptions[] = [
      {
        format: 'system',
        dir: 'dist',
        plugins: [htmlPlugin.api.addOutput('legacy')],
      },
      {
        format: 'es',
        dir: 'dist',
        plugins: [htmlPlugin.api.addOutput('modern')],
      },
    ];

    await testSnapshot({
      name: 'customize-filetype-multi-output',
      fileName: 'index.html',
      inputOptions,
      outputOptions,
    });
  });

  it('injects preload when there are no polyfills to inject', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-b.js"></script>`,
          },
        }),
        polyfillsLoader(),
      ],
    };

    await testSnapshot({
      name: 'no-polyfills',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('will retain attributes of script tags if there are no polyfills to inject', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js" keep-this-attribute></script>
            <script type="module" src="${relativeUrl}/fixtures/entrypoint-b.js"></script>`,
          },
        }),
        polyfillsLoader(),
      ],
    };

    await testSnapshot({
      name: 'no-polyfills-retain-attributes',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });

  it('can inject a polyfills loader as an external script', async () => {
    const inputOptions: RollupOptions = {
      plugins: [
        html({
          input: {
            html: `<script type="module" src="${relativeUrl}/fixtures/entrypoint-a.js"></script>`,
          },
        }),
        polyfillsLoader({
          polyfills: { hash: false, fetch: true },
          externalLoaderScript: true,
        }),
      ],
    };

    await testSnapshot({
      name: 'external-script',
      fileName: 'index.html',
      inputOptions,
      outputOptions: defaultOutputOptions,
    });
  });
});