parcel icon indicating copy to clipboard operation
parcel copied to clipboard

Parcel API Does Not Generate Types

Open leafoflegend opened this issue 3 years ago • 3 comments

🐛 bug report

I am unable to get a valid index.d.ts to generate. I am using the Parcel API. It is mostly undocumented in the API how one would go about this. I found this issue which gave me the idea to make a 'types' target, which does generate an index.ts file, but it has multiple export defaults, making it invalid.

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

Keep in mind that this is using the API. I get an error if I don't include a distDir, I've also tried having a top level types outside of targets with a dist/index.d.ts, but that wont work either.

{
   entry: `${SOME_VALID_ENTRY}`,
   targets: {
        'esm-node': {
            context: 'node',
            engines: {
                node: `>=${MODERN_NODE_VERSION}.0.0`,
            },
            outputFormat: 'esmodule',
            isLibrary: true,
            distDir: './dist/esm-node',
        },
        esm: {
            context: 'browser',
            engines: {
                browsers: BROWSERS_RULE,
            },
            outputFormat: 'esmodule',
            distDir: './dist/esm',
        },
        cjs: {
            context: 'node',
            engines: {
                node: `>=${LEGACY_NODE_VERSION}.0.0`,
            },
            outputFormat: 'commonjs',
            distDir: './dist/cjs',
        },
        types: {
            distDir: './dist',
        },
    },
}

🤔 Expected Behavior

I'd expect that given a TS file, a .d.ts file is generated. In the above config, that it is placed inside the ./dist directory.

😯 Current Behavior

Without specifying a types target, no types are produced, when included, it generates an invalid typescript file.

💁 Possible Solution

I'd like to have clear documentation and a setting for where to put the types. Perhaps it could even look like:

{
  typeDeclarationDest: './some/path/types.d.ts',
  targets: { /* blah blah */ }
}

🔦 Context

It is game breaking for my attempt at using parcel as part of a library in which it is expected to produce the declaration files (or atleast, move the ones typescript produces to the correct location).

💻 Code Sample

import { Parcel } from '@parcel/core';

const bundler = new Parcel({
  entry: `${SOME_VALID_ENTRY}.ts`,
  targets: {
        'esm-node': {
            context: 'node',
            engines: {
                node: `>=${MODERN_NODE_VERSION}.0.0`,
            },
            outputFormat: 'esmodule',
            isLibrary: true,
            distDir: './dist/esm-node',
        },
        esm: {
            context: 'browser',
            engines: {
                browsers: BROWSERS_RULE,
            },
            outputFormat: 'esmodule',
            distDir: './dist/esm',
        },
        cjs: {
            context: 'node',
            engines: {
                node: `>=${LEGACY_NODE_VERSION}.0.0`,
            },
            outputFormat: 'commonjs',
            distDir: './dist/cjs',
        },
    },
});

await bundler.run();

Do this with any valid typescript and a tsconfig.json and you will not receive a declaration file.

🌍 Your Environment

Software Version(s)
Parcel 2.8.3
Node 18.11.0
npm/Yarn npm 9.4.0
Operating System Mac OS Ventura 13.2.1

Love Parcel! ❤️

leafoflegend avatar Mar 08 '23 20:03 leafoflegend

Maybe related parcel build --target types when types is declared in targets doesn't produce a type declaration either

danieltroger avatar Apr 26 '23 14:04 danieltroger

I really wanted to have multiple target exports with types, and I had to do a very smelly solution. The idea is to simply replace the targets in package.json using replace-in-file package (which I had already included as dev dep for some other stuff).

For example, I have 3 targets at lib, lib/common and lib/contracts. I have a single source and 2 targets + type target as:

"source": "src/index.ts",
"types": "lib/index.d.ts",
"cjs": "lib/index.cjs",
"mjs": "lib/index.mjs",

My targets field is as follows:

"targets": {
    "cjs": {
      "outputFormat": "commonjs",
      "isLibrary": true,
      "context": "node"
    },
    "mjs": {
      "outputFormat": "esmodule",
      "isLibrary": true,
      "context": "node"
    }
  },

I wrote a small script that uses replace-in-file package to replace these lines from the current target to my desired target, and I specify that mapping with a step number.

const replace = require('replace-in-file');
const srcDir = 'src';
const outDir = 'lib';
async function main(from, to) {
  const entriesFrom = [
    `"source": "${srcDir}/${from}index.ts",`,
    `"types": "${outDir}/${from}index.d.ts",`,
    `"cjs": "${outDir}/${from}index.cjs",`,
    `"mjs": "${outDir}/${from}index.mjs",`,
  ];
  const entriesTo = [
    `"source": "${srcDir}/${to}index.ts",`,
    `"types": "${outDir}/${to}index.d.ts",`,
    `"cjs": "${outDir}/${to}index.cjs",`,
    `"mjs": "${outDir}/${to}index.mjs",`,
  ];

  const mappings = entriesFrom.map((v, i) => ({
    from: v,
    to: entriesTo[i],
  }));

  mappings.map(({from, to}) =>
    replace.sync({
      files: './package.json',
      from,
      to,
    })
  );
}

if (require.main === module) {
  if (process.argv.length !== 3) {
    throw new Error('Usage: node replace.cjs stepNo');
  }
  const steps = ['', 'common/', 'contracts/', ''];
  const stepNo = parseInt(process.argv[2]);
  if (stepNo === 0 || stepNo >= steps.length) {
    throw new Error('Make sure: 0 < step no < step count');
  }
  main(steps[stepNo - 1], steps[stepNo]);
}

My custom scripts in package.json is then:

 "prebuild": "yarn clean",
 "build": "parcel build && yarn step 1 && parcel build && yarn step 2 && parcel build && yarn step 3",
 "postbuild": "yarn check",
 "step": "node buildstep.cjs",
 "clean": "rimraf ./lib",

I HATE this solution and I hope things get fixed.

erhant avatar Jun 19 '23 09:06 erhant

This is an issue for me as well. Can't get types generated when using targets.

kolyasya avatar Dec 24 '23 03:12 kolyasya

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.

github-actions[bot] avatar Dec 23 '24 00:12 github-actions[bot]