Git Product home page Git Product logo

rollup-plugin-flat-dts's Introduction

Flatten .d.ts Files

NPM Build Status Code Quality GitHub Project API Documentation

Example Configuration

Add the following rollup.config.js:

import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import flatDts from 'rollup-plugin-flat-dts';
import sourcemaps from 'rollup-plugin-sourcemaps';
import ts from 'rollup-plugin-typescript2';

export default {
  input: './src/index.ts',
  plugins: [commonjs(), ts(), nodeResolve(), sourcemaps()],
  output: {
    format: 'esm',
    sourcemap: true,
    file: 'dist/index.js',
    plugins: [flatDts()],
  },
};

Then the command rollup --config ./rollup.config.js would transpile TypeScript files from src/ directory to dist/index.js, and generate dist/index.d.ts with flattened type definitions.

Limitations

This plugin flattens type definitions instead of merging them. This applies severe limitations to the source code:

  1. Every export in every TypeScript file considered exported from the package (i.e. part of public API).

    Mark internal exports (internal API) with @internal jsdoc tag to prevent this, or declare internal modules with internal option.

  2. Default exports supported only at the top level and in entry points (see below).

  3. Exported symbols should be unique across the code base.

  4. Exports should not be renamed when re-exporting them.

    Aliasing is still possible.

Project Structure

To adhere to these limitations the project structure could be like this:

  1. An index file (index.ts) is present in each source directory with exported symbols.

    Index file re-exports all publicly available symbols from the same directory with statements like export * from './file';.

    Index file also re-export all symbols from nested directories with statements like export * from './dir';

  2. All exported symbols that are not re-exported by index files considered part of internal API.

    Every such symbols has a jsdoc block containing @internal tag.

    Alternatively, the internal modules follow some naming convention. The internal option reflects this convention. E.g. internal: ['**/impl/**', '**/*.impl'] would treat all .impl.ts source files and files in impl/ directories as part of internal API.

  3. Rollup entry points are index files.

Configuration Options

flatDts({}) accepts configuration object with the following properties:

  • tsconfig - Either tsconfig.json file location relative to working directory, or parsed tsconfig.json contents.

    tsconfig.json by default.

  • compilerOptions - TypeScript compiler options to apply.

    Override the options from tsconfig.

  • file - Output .d.ts file name relative to output directory.

    index.d.ts by default.

  • moduleName - The module name to replace flattened module declarations with.

    Defaults to package name extracted from package.json.

  • entries - Module entries.

    A map of entry name declarations (see below).

  • lib - Whether to add triple-slash directives to refer the libraries used.

    Allowed values:

    • true to add an entry for each referred library from lib compiler option,
    • false (the default) to not add any library references,
    • an explicit list of libraries to refer.
  • refs - Whether to add file references.

    A file reference is added when one entry refers another one.

    true by default.

  • external - External module names.

    An array of external module names and their glob patterns. These names won't be changed during flattening process.

    This is useful for external module augmentation.

  • internal - Internal module names.

    An array of internal module names and their glob patterns. Internal module type definitions are excluded from generated .d.ts files.

Declaration Maps (Experimental)

When declarationMap compiler option enabled a declaration map file(s) (.d.ts.map) will be generated next to .d.ts ones:

import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import flatDts from 'rollup-plugin-flat-dts';
import sourcemaps from 'rollup-plugin-sourcemaps';
import ts from 'rollup-plugin-typescript2';

export default {
  input: './src/index.ts',
  plugins: [commonjs(), ts(), nodeResolve(), sourcemaps()],
  output: {
    format: 'esm',
    sourcemap: true,
    file: 'dist/index.js',
    plugins: [
      flatDts({
        compilerOptions: {
          declarationMap: true /* Generate declaration maps */,
        },
      }),
    ],
  },
};

Declaration maps can be used by IDE to navigate to TypeScript source file instead of type declaration when both available. This functionality relies on IDE heuristics, and declaration maps generated by this tool may not suit it fully. So, don't be surprised if that does not work as expected.

Multiple Entries

By default, the generated .d.ts file contains declare module statements with the same moduleName.

If your package has additional entry points then you probably want to reflect this in type definition. This can be achieved with entries option.

Here is an example configuration:

import commonjs from '@rollup/plugin-commonjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import flatDts from 'rollup-plugin-flat-dts';
import sourcemaps from 'rollup-plugin-sourcemaps';
import ts from 'rollup-plugin-typescript2';

export default {
  input: {
    main: './src/index.ts', // Main entry point
    node: './src/node/index.ts', // Node.js-specific API
    web: './src/browser/index.ts', // Browser-specific API
  },
  plugins: [commonjs(), ts(), nodeResolve(), sourcemaps()],
  output: {
    format: 'esm',
    sourcemap: true,
    dir: 'dist', // Place the output files to `dist` directory.
    entryFileNames: '[name].js', // Entry file names have `.js` extension.
    chunkFileNames: '_[name].js', // Chunks have underscore prefix.
    plugins: [
      flatDts({
        moduleName: 'my-package', // By default, exports belong to `my-package` module.
        entries: {
          node: {}, // All exports from `src/node` directory
          // belong to `my-package/node` sub-module.
          browser: {
            // All exports from `src/browser` directory
            as: 'web', // belong to `my-package/web` sub-module.
            // (Would be `my-package/browser` if omitted)
            lib: 'DOM', // Add `DOM` library reference.
            refs: false, // Do not add triple-slash file references to other entries.
            // Otherwise, a file reference will be added for each entry this one refers.
            file: 'web/index.d.ts', // Write type definitions to separate `.d.ts` file.
            // (Would be written to main `index.d.ts` if omitted)
          },
        },
      }),
    ],
  },
};

The package.json would contain the following then:

{
  "name": "my-package",
  "type": "module",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": "./dist/main.js",
    "./node": "./dist/node.js",
    "./web": "./dist/web.js"
  }
}

Standalone Usage

The API is available standalone, without using Rollup:

import { emitFlatDts } from 'rollup-plugin-flat-dts/api';

emitFlatDts({
  /* Type definition options */
})
  .then(flatDts => {
    if (flatDts.diagnostics.length) {
      console.error(flatDts.formatDiagnostics());
    }
    return flatDts.writeOut();
  })
  .catch(error => {
    console.error('Failed to generate type definitions', error);
    process.exit(1);
  });

Algorithm Explained

The plugin algorithm is not very sophisticated, but simple and straightforward. This made it actually usable on my projects, where other tools failed to generate valid type definitions.

The simplicity comes at a price. So, this plugin applies limitations on code base rather trying to resolve all non-trivial cases.

So, here what this plugin is doing:

  1. Generates single-file type definition by overriding original tsconfig.json options with the following:

    {
      // Avoid extra work
      "checkJs": false,
      // Ensure ".d.ts" modules are generated
      "declaration": true,
      // Prevent output to declaration directory
      "declarationDir": null,
      // Skip ".js" generation
      "emitDeclarationOnly": true,
      // Single file emission is impossible with this flag set
      "isolatedModules": false,
      // Generate single file
      // `System`, in contrast to `None`, permits the use of `import.meta`
      "module": "System",
      // When set to "Node16" or "NodeNext", or when unspecified
      // Otherwise, it conflicts with SystemJS
      "moduleResolution": "Node",
      // Always emit
      "noEmit": false,
      // Skip code generation when error occurs
      "noEmitOnError": true,
      // SystemJS does not support JSON module imports
      "resolveJsonModule": false,
      // Ignore errors in library type definitions
      "skipLibCheck": true,
      // Always strip internal exports
      "stripInternal": true,
      // Unsupported by SystemJS
      "verbatimModuleSyntax": false
    }
  2. The generated file consists of declare module "path/to/file" { ... } statements. One such statement per each source file.

    The plugin replaces all "path/to/file" references with corresponding module name. I.e. either with ${packageName}, or ${packageName}/${entry.as} for matching entry point.

  3. Updates all import and export statements and adjusts module references.

    Some imports and exports removed along the way. E.g. there is no point to import to the module from itself, unless the named import or export assigns an alias to the imported symbol.

  4. Updates inner module declarations.

    Just like 2., but also expands declarations if inner module receives the same name as enclosing one.

  5. Removes module declarations that became (or was originally) empty.

Other Tools

See more here.

rollup-plugin-flat-dts's People

Contributors

surol avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

rollup-plugin-flat-dts's Issues

React native style ".native.ts" will create type overlaps

Noticed this in SnackUI today. The use of the same exports in a .native.ts file will cause overlaps in the bundled output.

This one seems a bit simpler than sourcemaps, as I think we could have simple rules to decide which to select.

Not sure which is better, allowing config based on filename, or more programmatic (filterTypes: (fileName, types) => ) maybe).

Typescript 4.5 - sources always empty "Error running flat dts Failed to emit type definitions Error: Failed to emit type definitions"

Getting this as of 4.5, I added logging inside to see why but nothing seems off it reads the right config.

Error running flat dts Failed to emit type definitions Error: Failed to emit type definitions
    at Object.writeOut (/Users/n8/dish/node_modules/rollup-plugin-flat-dts/dist/flat-dts.api.cjs:235:35)

For some reason this line:

const { options, errors, fileNames: files } = ts__default['default'].parseJsonConfigFileContent(config, ts__default['default'].sys, dirName);

Just returns an empty array always. Was working fine before.

How to support json import

Thank you for your plugin.
I am now use the plugin with Typescript 5.
Because there is some '*.json' in my project, so I set

{
  "resolveJsonModule": true,
  "module": "ESNext"
}

in my tsconfig.

but the plugin print error like this:

[!] (plugin flat-dts) RollupError: error TS5071: Option '--resolveJsonModule' can only be specified when module code generation is 'commonjs', 'amd', 'es2015' or 'esNext'.

I saw the plugin over the config module to 'system',so the error print.

So how can i use the plugin with json import like import './lang.json;' in ts and tsx.

If the json can not support, could over the tsconfig resolveJsonModule to false, because the module: system can not resolve json.

declarationMap source file relative path problem

Hi,

Great work on this plugin!

My index.d.ts.map is being generated with wrong source files path.
The output dir is ./dist/esm/, and the generated index.d.ts.map contains the following:
{"version":3,"sources":["src/styles.ts","src/Separator.tsx"],"names":[],"mappings":";IAEA,M ........
I belive the sources array should be ["../../src/styles.ts","../../src/Separator.tsx"], so the IDE jump-click works.

do you belive it is a configuration related issue?

ESM support

Trying to use rollup-plugin-flat-dts with an Node.js project published in ESM syntax and TypeScript 5 and getting the following errors:

  • TS5053: Option 'outFile' cannot be specified with option 'verbatimModuleSyntax'.
  • TS5105: Option 'verbatimModuleSyntax' cannot be used when 'module' is set to 'UMD', 'AMD', or 'System'.
  • TS2441: Duplicate identifier 'require'. Compiler reserves name 'require' in top level scope of a module.

I could try to work around the first two, but the last one is not so easy. This is an ESM package and to have require I need to call createRequire(). rollup-plugin-flat-dts does not allow that. Hm.. Tricky.

Maintaining the ability to jump-click to original source file?

Love love love the plugin, just moved over to it and it improved CI builds from ~2m to 11s because we can parallelize it fully now!

One downgrade from before was that having the separate .d.ts files meant in vscode ctrl+click would jump to the original file definition rather than the type file, which is really nice, but now it will jump to the bundled .d.ts file instead of the original.

Any idea if this is possible to make work with this strategy?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.