Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

emitDeclarationOnly in v0.33.0 -- what if I want an empty chunk and only declarations? #411

Closed
BitPatty opened this issue Aug 23, 2022 · 6 comments
Labels
kind: question This is a usage or similar question kind: support Asking for support with something or a specific use case scope: integration Related to an integration, not necessarily to core (but could influence core) solution: intended behavior This is not a bug and is expected behavior solution: workaround available There is a workaround available for this issue

Comments

@BitPatty
Copy link

Troubleshooting

  1. Does tsc have the same output? If so, please explain why this is incorrect behavior

No

  1. Does your Rollup plugin order match this plugin's compatibility? If not, please elaborate

Yes

  1. Can you create a minimal example that reproduces this behavior? Preferably, use this environment for your reproduction

https://stackblitz.com/edit/rpt2-repro-1kadjh?file=package.json

What happens and why it is incorrect

The build crashes if emitDeclarationOnly is set to true in overrides or tsconfig. tsc produces the correct output. Used to work until v0.33.0.

Environment

Versions

  System:
    OS: Linux 5.4 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
    CPU: (4) x64 Intel(R) Xeon(R) Platinum 8272CL CPU @ 2.60GHz
    Memory: 5.13 GB / 7.77 GB
    Container: Yes
    Shell: 5.1.4 - /bin/bash
  Binaries:
    Node: 18.4.0 - /usr/local/share/nvm/versions/node/v18.4.0/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 8.18.0 - /usr/local/share/nvm/versions/node/v18.4.0/bin/npm
  npmPackages:
    rollup: 2.78.0 => 2.78.0 
    rollup-plugin-typescript2: 0.33.0 => 0.33.0 
    typescript: 4.7.4 => 4.7.4 

rollup.config.js

:
import typescript from 'rollup-plugin-typescript2';

export default {
  input: './src/index.ts',
  output: {
    file: './dist/index.ts',
    format: 'esm',
    exports: 'named',
  },
  plugins: [
    typescript({
      verbosity: 3,
      clean: true,
    }),
  ],
};

tsconfig.json

:
{
  // https://github.com/agilgur5/tsconfig
  "extends": "@agilgur5/tsconfig/src/tsconfig.library.json",
  // exclude node_modules (the default), dist dir, coverage dir, and example for now
  "exclude": ["node_modules/", "dist/", "coverage/", "example/"],
  // all TS files in the src/ dir
  "include": ["src/**/*"],
  // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
  "compilerOptions": {
    // output to dist/ dir
    "outDir": "./dist/",
    // match output dir to input dir. e.g. dist/index instead of dist/src/index
    "rootDir": "./src",
    "emitDeclarationOnly": true
  }
}

package.json

:
{
  "name": "rpt2-repro",
  "version": "0.0.0",
  "scripts": {
    "clean": "rm -rf dist/",
    "build": "rollup -c",
    "tsc": "tsc"
  },
  "devDependencies": {
    "@agilgur5/tsconfig": "^0.0.2",
    "rollup": "^2.75.6",
    "rollup-plugin-typescript2": "^0.33.0",
    "typescript": "^4.7.3"
  }
}

plugin output with verbosity 3

:
./src/index.ts → ./dist/index.ts...
rpt2: built-in options overrides: {
    "noEmitHelpers": false,
    "importHelpers": true,
    "noResolve": false,
    "noEmit": false,
    "noEmitOnError": false,
    "inlineSourceMap": false,
    "outDir": "/home/projects/rpt2-repro-1kadjh/node_modules/.cache/rollup-plugin-typescript2/placeholder",
    "moduleResolution": 2,
    "allowNonTsExtensions": true,
    "module": 5
}
rpt2: parsed tsconfig: {
    "options": {
        "strict": true,
        "allowUnusedLabels": false,
        "allowUnreachableCode": false,
        "exactOptionalPropertyTypes": true,
        "noFallthroughCasesInSwitch": true,
        "noImplicitOverride": true,
        "noImplicitReturns": true,
        "noPropertyAccessFromIndexSignature": true,
        "noUncheckedIndexedAccess": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "importsNotUsedAsValues": 2,
        "checkJs": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "outDir": "/home/projects/rpt2-repro-1kadjh/node_modules/.cache/rollup-plugin-typescript2/placeholder",
        "sourceMap": true,
        "moduleResolution": 2,
        "resolveJsonModule": true,
        "jsx": 2,
        "noEmit": false,
        "declaration": true,
        "declarationMap": true,
        "rootDir": "/home/projects/rpt2-repro-1kadjh/src",
        "emitDeclarationOnly": true,
        "configFilePath": "/home/projects/rpt2-repro-1kadjh/tsconfig.json",
        "noEmitHelpers": false,
        "importHelpers": true,
        "noResolve": false,
        "noEmitOnError": false,
        "inlineSourceMap": false,
        "allowNonTsExtensions": true,
        "module": 5
    },
    "fileNames": [
        "/home/projects/rpt2-repro-1kadjh/src/index.ts"
    ],
    "typeAcquisition": {
        "enable": false,
        "include": [],
        "exclude": []
    },
    "raw": {
        "extends": "@agilgur5/tsconfig/src/tsconfig.library.json",
        "exclude": [
            "node_modules/",
            "dist/",
            "coverage/",
            "example/"
        ],
        "include": [
            "src/**/*"
        ],
        "compilerOptions": {
            "outDir": "./dist/",
            "rootDir": "./src",
            "emitDeclarationOnly": true
        },
        "compileOnSave": false
    },
    "errors": [],
    "wildcardDirectories": {
        "/home/projects/rpt2-repro-1kadjh/src": 1
    },
    "compileOnSave": false
}
rpt2: typescript version: 4.7.3
rpt2: tslib version: 2.4.0
rpt2: rollup version: 2.75.6
rpt2: rollup-plugin-typescript2 version: 0.33.0
rpt2: plugin options:
{
    "check": true,
    "verbosity": 3,
    "clean": true,
    "cacheRoot": "/home/projects/rpt2-repro-1kadjh/node_modules/.cache/rollup-plugin-typescript2",
    "include": [
        "*.ts+(|x)",
        "**/*.ts+(|x)"
    ],
    "exclude": [
        "*.d.ts",
        "**/*.d.ts"
    ],
    "abortOnError": true,
    "rollupCommonJSResolveHack": false,
    "useTsconfigDeclarationDir": false,
    "tsconfigOverride": {},
    "transformers": [],
    "tsconfigDefaults": {},
    "objectHashIgnoreUnknownHack": false,
    "cwd": "/home/projects/rpt2-repro-1kadjh",
    "typescript": "version 4.7.3"
}
rpt2: rollup config:
{
    "external": [],
    "input": "./src/index.ts",
    "plugins": [
        {
            "name": "rpt2"
        },
        {
            "name": "stdin"
        }
    ],
    "output": [
        {
            "exports": "named",
            "file": "./dist/index.ts",
            "format": "esm",
            "plugins": []
        }
    ]
}
rpt2: tsconfig path: /home/projects/rpt2-repro-1kadjh/tsconfig.json
rpt2: included:
[
    "*.ts+(|x)",
    "**/*.ts+(|x)"
]
rpt2: excluded:
[
    "*.d.ts",
    "**/*.d.ts"
]
rpt2: transpiling '/home/projects/rpt2-repro-1kadjh/src/index.ts'
rpt2: generated declarations for '/home/projects/rpt2-repro-1kadjh/src/index.ts'
rpt2: emitDeclarationOnly enabled, not transforming TS'
[!] (plugin rpt2) Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
src/index.ts (4:29)
2: // run `npm run tsc` in the terminal to type-check this file with tsc
3: 
4: export default function sum(a: number, b: number) {
                                ^
5:   return a + b;
6: }

    at error (/home/projects/rpt2-repro-1kadjh/node_modules/rollup/dist/shared/rollup.js:198:30)
    at Module.error (/home/projects/rpt2-repro-1kadjh/node_modules/rollup/dist/shared/rollup.js:12553:16)
    at Module.tryParse (/home/projects/rpt2-repro-1kadjh/node_modules/rollup/dist/shared/rollup.js:12930:25)
    at Module.setSource (/home/projects/rpt2-repro-1kadjh/node_modules/rollup/dist/shared/rollup.js:12835:24)
    at ModuleLoader.addModuleSource (/home/projects/rpt2-repro-1kadjh/node_modules/rollup/dist/shared/rollup.js:22309:20)
@agilgur5 agilgur5 changed the title emitDeclarationOnly throws in v0.33.0 emitDeclarationOnly throws in v0.33.0 -- needs another plugin (intended) Aug 23, 2022
@agilgur5 agilgur5 added solution: intended behavior This is not a bug and is expected behavior scope: integration Related to an integration, not necessarily to core (but could influence core) kind: support Asking for support with something or a specific use case labels Aug 23, 2022
@agilgur5 agilgur5 changed the title emitDeclarationOnly throws in v0.33.0 -- needs another plugin (intended) emitDeclarationOnly throws in v0.33.0 -- needs a different TS transpiler (intended) Aug 23, 2022
@agilgur5
Copy link
Collaborator

agilgur5 commented Aug 23, 2022

This is a new feature in 0.33.0, supporting emitDeclarationOnly. Previously, this tsconfig option was entirely ignored.

Per the log you provided, it states "rpt2: emitDeclarationOnly enabled, not transforming TS". As such, you need another plugin to transform TS to JS when using emitDeclarationOnly.

Per the docs, this feature is meant to be used together with another plugin, such as @rollup/plugin-babel, rollup-plugin-esbuild, rollup-plugin-swc, etc.
With this use-case, you have rpt2 use the TS compiler to type-check and create declarations, while another plugin does the TS -> JS transform.

Per the release notes, this was implemented in #366 to support the highly requested #268.

As such, this is not a bug, but very much intended behavior.

If you want to keep emitDeclarationOnly in your tsconfig for tsc, but still want rpt2 to do TS -> JS transformation, please use tsconfigOverride to reset emitDeclarationOnly to false for rpt2.

@agilgur5 agilgur5 added the solution: workaround available There is a workaround available for this issue label Aug 23, 2022
@BitPatty
Copy link
Author

Per the log you provided, it states "rpt2: emitDeclarationOnly enabled, not transforming TS". As such, you need another plugin to transform TS to JS when using emitDeclarationOnly.

Hope I don't misunderstand: What if I only want to emit declarations and not transpile TS at all?

While it did work before and warn about an empty chunk being generated, it still did emit the declaration as expected. And though tsc itself could be used to build the declaration, the rpt2 simplified the process up until now when using multiple build targets.

@agilgur5
Copy link
Collaborator

agilgur5 commented Aug 25, 2022

While it did work before and warn about an empty chunk being generated, it still did emit the declaration as expected.

Oh. I didn't realize that anyone actually wanted an empty chunk. #268 showed that most folks were pretty confused by the resulting empty chunk. But I guess everyone's got their own workflow 😅

And though tsc itself could be used to build the declaration, the rpt2 simplified the process up until now when using multiple build targets.

Any chance you could provide a small example of where rpt2 simplifies the process vs. using tsc for multi-build with only declarations? I think I'm missing a detail here, so an example would be great to wrap my head around.

What if I only want to emit declarations and not transpile TS at all?

So this is still possible in 0.33.0 in a slightly hacky way. As long as your TS files are in your tsconfig include, rpt2 will still emit their declarations, even if they're not directly part of the Rollup bundle (because that's how tsc works as well).

So what you could do is use an empty file for Rollup's output.file EDIT: woops, I meant input, which will result in the same behavior as an empty chunk, and then rpt2 will still emit declarations for anything in your tsconfig include.

Does that satisfy your use-case? It's a bit hacky, but I do think the intent is a little more clear that way (e.g. empty file -> empty chunk). Most people seemed to have been pretty confused by the empty chunk behavior before and that didn't seem intuitive to them.

@agilgur5 agilgur5 changed the title emitDeclarationOnly throws in v0.33.0 -- needs a different TS transpiler (intended) emitDeclarationOnly in v0.33.0 -- what if I want an empty chunk and only declarations? Aug 25, 2022
@agilgur5 agilgur5 added kind: question This is a usage or similar question controversial kind: discussion Discussion on changes to make labels Aug 25, 2022
@BitPatty
Copy link
Author

Any chance you could provide a small example of where rpt2 simplifies the process vs. using tsc for multi-build with only declarations? I think I'm missing a detail here, so an example would be great to wrap my head around.

In some libraries which I cross build to ESM and CJS I use a split rollup configuration to generate the individual outputs with the same tsconfig. Essentially only emitting the declaration once into /types, generating the following output structure:

image

The configuration boils down to something like this: https://stackblitz.com/edit/rpt2-repro-e2gczu?file=rollup.config.js, i.e:

rollup.config.js

:
import typescript from 'rollup-plugin-typescript2';

export default [
  {
    input: './src/index.ts',
    output: {
      file: './dist/esm/index.js',
      format: 'esm',
      exports: 'named',
      sourcemap: true,
    },
    plugins: [
      typescript({
        verbosity: 3,
        clean: true,
        tsconfigOverride: {
          compilerOptions: {
            declaration: false,
          },
        },
      }),
    ],
  },
  {
    input: './src/index.ts',
    output: {
      file: './dist/cjs/index.js',
      format: 'cjs',
      exports: 'named',
      sourcemap: true,
    },
    plugins: [
      typescript({
        verbosity: 3,
        clean: true,
        tsconfigOverride: {
          compilerOptions: {
            declaration: false,
          },
        },
      }),
    ],
  },
  {
    input: './src/index.ts',
    output: {
      file: './dist/types/index.d.ts',
    },
    plugins: [
      typescript({
        verbosity: 3,
        clean: true,
        tsconfigOverride: {
          compilerOptions: {
            declaration: true,
            emitDeclarationOnly: true,
          },
        },
      }),
    ],
  },
];

(Full configuration: https://github.com/BitPatty/ts-library-template/blob/2a14eb539eaa6af659467cca5595972e229fa288/rollup.config.js)

So what you could do is use an empty file for Rollup's output.file, which will result in the same behavior as an empty chunk, and then rpt2 will still emit declarations for anything in your tsconfig include.

In my case I already have the declaration file as output.file, so not sure how/if this would work.

It's a bit hacky, but I do think the intent is a little more clear that way

Not sure if my current solution would be considered hacky as well :)

Of course I could also emit the declaration with both the ESM and the CJS output or copy back the types in a later step, but I figured it'd be nicer to just have the declaration be available at one place and rpt2 made it quite easy to set up this target structure without any extra configuration.

@agilgur5 agilgur5 removed controversial kind: discussion Discussion on changes to make labels Aug 25, 2022
@agilgur5
Copy link
Collaborator

agilgur5 commented Aug 25, 2022

In my case I already have the declaration file as output.file, so not sure how/if this would work.

Oh woops, my bad, I meant the input. Point the input to an empty file.

(I happened to have been changing a Rollup config at the same time, so I screwed that up, sorry! Of course, the output.file doesn't exist yet).

rollup.config.js

Thanks for providing a detailed example, that made it much easier to understand!
There are in fact other alternatives to achieve this (which was much easier to see at-a-glance with an example!).

A commonly used one by rpt2 users is to set useTsconfigDeclarationDir: true and declarationDir: './dist/types'. For instance:

// rollup.config.js
import typescript from 'rollup-plugin-typescript2';

export default [{
  input: './src/index.ts',
  output: [{
    file: './dist/esm/index.js',
    format: 'esm',
    exports: 'named',
    sourcemap: true,
  }, {
    file: './dist/cjs/index.js',
    format: 'cjs',
    exports: 'named',
    sourcemap: true,
  }],
  plugins: [
    typescript({
      verbosity: 3,
      clean: true,
      useTsconfigDeclarationDir: true,
      tsconfigOverride: {
        compilerOptions: {
          declarationDir: './dist/types',
        },
      },
    }),
  ],
}];

Note that I'm also using Rollup's multi-output feature here for ESM + CJS, which should optimize your build a bit too, since Rollup won't need to analyze the input a second time around or re-run any input plugins.

The caveat with useTsconfigDeclarationDir is that other plugins that process declarations won't work (it writes directly to the declarationDir, bypassing Rollup's emit phase), but it doesn't seem like you need that in your case anyway. And it seems that the vast majority of users are okay with that caveat/trade-off as well, so I think I can safely recommend that option.

@BitPatty
Copy link
Author

Thanks a lot for your time and the hint regarding the useTsconfigDeclarationDir option. With that configuration it works again (and better) as before 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: question This is a usage or similar question kind: support Asking for support with something or a specific use case scope: integration Related to an integration, not necessarily to core (but could influence core) solution: intended behavior This is not a bug and is expected behavior solution: workaround available There is a workaround available for this issue
Projects
None yet
Development

No branches or pull requests

2 participants