typescript - Importera från underkataloger för ett javascript-paket


Jag har ett TypeScript-bibliotek som består av flera mappar. Varje mapp innehåller en index.ts-fil som exporterar viss affärslogik. Jag försöker paketera detta med Rollup för att uppnå detta beteende på anropsplatsen:

import { Button, ButtonProps } from 'my-lib/button'
import { Input, Textarea } from 'my-lib/input'
import { Row, Column } from 'my-lib/grid'

Detta är katalogstrukturen:

Jag har en huvud-index.ts under src/ som innehåller:

export * from './button';
export * from './input';
export * from './grid';

Med den här stilen kan jag göra:

import { Button, Input, InputProps, Row, Column } from 'my-lib'

Men jag vill inte ha detta. Jag vill ha åtkomst till varje modul via deras namespace. Om jag tar bort exports från index.ts-filen, är allt jag kan göra:

import { Button } from 'my-lib/dist/button'

vilket är något jag inte såg tidigare. Att lägga till dist/ i importuttalandet betyder att jag får åtkomst till modulerna via en relativ sökväg. Jag vill ha my-lib/Button.

Jag använder rollup. Jag försökte använda alias-plugin men det fungerade inte. Nedan är min rollup-konfiguration:

const customResolver = resolve({
  extensions: ['ts'],
});

export default {
  input: `src/index.ts`,
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      sourcemap: true,
      // plugins: [terser()],
    },
    {
      file: pkg.module,
      format: 'es',
      sourcemap: true,
      plugins: [terser()],
    },
  ],
  // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
  external: [],
  watch: {
    include: 'src/**',
  },
  plugins: [
    // Allow json resolution
    json(),
    // Compile TypeScript files
    typescript({ useTsconfigDeclarationDir: true }),
    // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
    commonjs(),
    // Allow node_modules resolution, so you can use 'external' to control
    // which external modules to include in the bundle
    // https://github.com/rollup/rollup-plugin-node-resolve#usage
    resolve(),

    // Resolve source maps to the original source
    sourceMaps(),
    alias({
      entries: [
        { find: 'my-lib/button', replacement: './dist/button' },
        { find: 'my-lib/input', replacement: './dist/input' },
        { find: 'my-lib/grid', replacement: './dist/grid' },
      ],
      customResolver,
    }),
  ],
};

Och detta är tsconfig-filen:

{
  "compilerOptions": {
    "target": "es5",
    "module": "ES6",
    "lib": ["ES2017", "ES7", "ES6", "DOM"],
    "declaration": true,
    "declarationDir": "dist",
    "outDir": "dist",
    "sourceMap": true,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "allowJs": false,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "baseUrl": "./src",
    "paths": {
      "my-lib/button": ["./src/button"],
      "my-lib/input": ["./src/input"],
      "my-lib/grid": ["./src/grid"]
    }
  },
  "exclude": ["node_modules", "dist", "**/*.test.ts"],
  "include": ["src/**/*.ts"]
}

Jag vet inte hur man uppnår samma struktur som lodash/xxx eller material-ui/yyy med rollup.

Folk föreslår alias eller namngivna exporter men jag kunde inte få det att fungera.

Det närmaste till mitt problem är frågan nedan:

Importera från undermapp i npm-paket

Jag vill uppnå samma sak men med typescript och rollup.

Jag tror att jag missar något, tack.

Svar #1

Detta är möjligt, men kräver några extra steg. Som nämnts ovan, är detta tillvägagångssättet som används av Material-UI.

Tricket är att publicera en kuraterad dist-mapp, snarare än rotmappen i ditt repo.

Byggnad

För att börja med, låt oss bara vara tydliga med att det inte spelar någon roll om ditt bibliotek är byggt med CommonJS eller ESM. Detta handlar om modulupplösning.

Låt oss anta att projektet heter my-package.

Nu kommer de flesta projekt, efter att vi har byggt src/ till dist/, att ha

my-package
  package.json
  src/
    index.js
  dist/
    index.js

och i package.json

"main": "dist/index.js"

eller för esm

"module": "dist/index.js"

Publicering

De flesta projekt lägger bara till .npmignore och publicerar projektets rot, så när det installeras hamnar projektet i node_modules på följande sätt:

node_modules
  my-package/
    package.json
    dist/
      index.js

Löser

När det är installerat, överväg denna import:

import myProject from "my-project";

Modulupplösaren kommer att göra detta (ganska förenklat, eftersom hela algoritmen är irrelevant här):

  • Gå till node_modules
  • Hitta my-project
  • Ladda package.json
  • Returnera filen i main eller module

Vilket kommer att fungera eftersom vi har

node_modules
  my-package/
    package.json
    dist/
      index.js

Lösa delvägar

import something from "my-project/something";

Upplösningsalgoritmen kommer att fungera med

node_modules
  my-project/
    somthing.js

också med

node_modules
  my-project/
    something/
      index.js

och med

node_modules
  my-project/
    something/
      package.json

där det i det senare fallet återigen kommer att titta på main eller module.

Men vi har:

node_modules
  my-package/
    package.json
    dist/
      index.js

Tricket

Tricket är, istället för att publicera din projektrot med sin dist-mapp, att "franka" dist-mappen och publicera dist-mappen med npm publish dist istället.

Frank (som i frank a letter) betyder att du behöver skapa en package.json i din dist-mapp; lägg till README.md LICENSE osv.

Ett ganska kort exempel på hur detta görs kan hittas här.

Så, med tanke på att vi hade efter byggandet:

node_modules
  my-package/
    package.json
    dist/
      index.js
      something.js

När det väl publicerats får vi

node_modules
  my-project/
    package.json
    index.js
    something.js

Där package.json är den noggrant utvalda.

Svar #2

För det första är den enda skillnaden mellan

import { Button } from 'my-lib/dist/button'

och

import { Button } from 'my-lib/button'

är bara ett ytterligare katalognivå.

När det är sagt, tills du har "outDir": "dist", i din tsconfig.json-fil behöver du lägga till dist/ i dina import-satser.

Faktiskt, båda de bibliotek du tagit som exempel distribueras med filer i rotkatalogen: lodash har direkt js-filer i roten, medan material-ui inte har något outDir-alternativ i sin tsconfig.json-fil (vilket betyder att skriva ut filer till rotkatalogen).

Hoppas det här hjälper.

Relaterade artiklar