parcel-bundler / parcel

The zero configuration build tool for the web. 📦🚀
https://parceljs.org
MIT License
43.46k stars 2.27k forks source link

Building Multiple Target in One Script with API: First Target Persists #1771

Closed Hoishin closed 4 years ago

Hoishin commented 6 years ago

🐛 bug report

When using API, building for multiple targets (e.g. node and browser) in one script with API results in second or later target not working.

🎛 Configuration (.babelrc, package.json, cli command)

.babelrc: I don't have one

package.json:

{
    "private": true,
    "dependencies": {
        "@material-ui/core": "^1.4.0",
        "@material-ui/icons": "^1.1.0",
        "app-root-path": "^2.1.0",
        "axios": "^0.18.0",
        "crypto": "^1.0.1",
        "del": "^3.0.0",
        "downshift": "^2.0.14",
        "load-json-file": "^5.0.0",
        "lodash": "^4.17.10",
        "oauth-1.0a": "^2.2.4",
        "react": "^16.4.1",
        "react-dom": "^16.4.1",
        "source-map-support": "^0.5.6",
        "styled-components": "^3.3.3",
        "write-json-file": "^2.3.0"
    },
    "devDependencies": {
        "@types/app-root-path": "^1.2.4",
        "@types/load-json-file": "^2.0.7",
        "@types/lodash": "^4.14.112",
        "@types/node": "^10.5.2",
        "@types/react": "^16.4.6",
        "@types/react-dom": "^16.0.6",
        "@types/source-map-support": "^0.4.1",
        "@types/write-json-file": "^2.2.1",
        "json-schema-to-typescript": "^5.5.0",
        "parcel-bundler": "^1.9.7",
        "prettier": "^1.13.7",
        "typescript": "^2.9.2"
    }
}

cli command: Not using CLI

build script:

const Bundler = require('parcel-bundler');
const appRoot = require('app-root-path');
const del = require('del');

const bundleFor = async ({input, output, target = 'browser'}) => {
    const entryFiles = appRoot.resolve(input);
    const outDir = appRoot.resolve(output);
    const options = {
        target,
        outDir,
        publicUrl: './',
    };

    await del(outDir);
    const bundler = new Bundler(entryFiles, options);
    await bundler.bundle();
};

const bundlers = [
    () =>
        bundleFor({
            input: 'src/dashboard/*.html',
            output: 'dashboard',
        }),

    () =>
        bundleFor({
            input: 'src/twitter-callback/*.html',
            output: 'twitter-callback',
        }),

    () =>
        bundleFor({
            input: 'src/extension/index.ts',
            output: 'extension',
            target: 'node',
        }),
];

if (process.env.NODE_ENV === 'production') {
    (async () => {
        for (const bundler of bundlers) {
            await bundler();
        }
    })();
} else {
    for (const bundler of bundlers) {
        bundler();
    }
}

🤔 Expected Behavior

It should bundle each entrypoint with the respective target.

😯 Current Behavior

It appears the later entrypoints are bundled with first entrypoint's target.

node -> browser

If I bundle with node target first, it sometimes warns during bundling, and doesn't work in run-time for both generated bundles.

Warning during bundling:

⚠️  /Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/node_modules/source-map-support/source-map-support.js:94:33: Cannot statically evaluate fs argument
  92 |     // Otherwise, use the filesystem
  93 |     try {
> 94 |       contents = fs.readFileSync(path, 'utf8');
     |                                  ^
  95 |     } catch (er) {
  96 |       contents = '';
  97 |     }
⚠️  /Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/node_modules/source-map-support/source-map-support.js:412:35: Cannot statically evaluate fs argument
  410 |     if (!contents && fs && fs.existsSync(source)) {
  411 |       try {
> 412 |         contents = fs.readFileSync(source, 'utf8');
      |                                    ^
  413 |       } catch (er) {
  414 |         contents = '';
  415 |       }

Runtime error in Node:

 TypeError: Cannot read property 'filename' of undefined
    at Object.parcelRequire.../../node_modules/app-root-path/browser-shim.js (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/node_modules/app-root-path/browser-shim.js:3:53)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at localRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:54:14)
    at Object.parcelRequire.../extension/twitter.ts.url (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/src/extension/twitter.ts:7:1)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at localRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:54:14)
    at Object.parcelRequire.../extension/index.ts.source-map-support/register (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/src/extension/index.ts:6:1)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at parcelRequire.../../node_modules/source-map-support/source-map-support.js (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:80:5)

Runtime error in browser:

Uncaught ReferenceError: process is not defined
    at Object.parcelRequire.../../node_modules/parcel-bundler/src/builtins/hmr-runtime.js (tech.tsx:54)
    at newRequire (tech.4b3e7f23.js:48)
    at parcelRequire.../lib/react-devtools.ts (tech.4b3e7f23.js:80)
    at tech.4b3e7f23.js:106

browser -> node

If I bundle with browser target first, it doesn't work in run-time in Node.

Runtime error in Node:

 TypeError: Cannot read property 'prototype' of undefined
    at patch (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/node_modules/graceful-fs/graceful-fs.js:166:54)
    at Object.parcelRequire.../../node_modules/graceful-fs/graceful-fs.js.fs (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/node_modules/graceful-fs/graceful-fs.js:27:18)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at localRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:54:14)
    at Object.parcelRequire.../../node_modules/load-json-file/index.js.path (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/node_modules/load-json-file/index.js:3:12)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at localRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:54:14)
    at Object.parcelRequire.../extension/twitter.ts.url (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/src/extension/twitter.ts:6:1)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at localRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:54:14)
    at Object.parcelRequire.index.ts../checklist (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/src/extension/index.ts:6:1)
    at newRequire (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:48:24)
    at parcelRequire.../extension/default/checklist.ts (/Users/kei/Development/rtainjapan/rtaij-nodecg/bundles/rtainjapan-layouts/extension/index.js:80:5)

💁 Possible Solution

It looks the bundler shares some context under the hood. When run for browser target, it remains targeting browser and vice versa.

🔦 Context

It would be nice to be able to bundle different target at once. I set up multiple script for each bundle output but it gets annoying as output destinations increases.

💻 Code Sample

https://github.com/RTAinJapan/rtainjapan-layouts

🌍 Your Environment

Software Version(s)
Parcel 1.9.7
Node v10.6.0
Yarn 1.7.0
Operating System Mac OS X 10.13.6
Hoishin commented 6 years ago

As a workaround, I was able to do this:

const {fork} = require('child_process');

const isProduction = process.env.NODE_ENV === 'production';
const command = isProduction ? 'build' : 'watch';
const detailedReport = isProduction ? ['--detailed-report'] : [];

fork('./node_modules/.bin/parcel', [
    command,
    'src/extension/index.ts',
    '--target',
    'node',
    '--out-dir',
    'extension',
    '--public-url',
    './',
    ...detailedReport,
])

fork('./node_modules/.bin/parcel', [
    command,
    'src/dashboard/*.html',
    '--out-dir',
    'dashboard',
    '--public-url',
    './',
    ...detailedReport,
])

fork('./node_modules/.bin/parcel', [
    command,
    'src/twitter-callback/*.html',
    '--out-dir',
    'twitter-callback',
    '--public-url',
    './',
    ...detailedReport,
])

Not the best way but it at least works.

KeineLimonade commented 6 years ago

I think the problem arises from the cache. Files are cached after transformation, so a file is transpiled the first time for one target and the second time it is used straight from cache, regardless of the target.

Try adding the cache: false option to your options object.

github-actions[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.