Импорт из подпапок для пакета javascript

У меня есть библиотека машинописных текстов, состоящая из нескольких папок. Каждая папка содержит файл index.ts, который экспортирует некоторую бизнес-логику. Я пытаюсь связать это с накопительным пакетом, чтобы добиться такого поведения на сайте вызова:

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

Это структура каталогов:

введите здесь описание изображения

У меня есть основной index.ts под src/, который содержит:

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

В этом стиле я могу:

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

Но я не хочу этого. Я хочу получить доступ к каждому модулю по их пространствам имен. Если я удалю экспорт из файла index.ts, все, что я смогу сделать, это:

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

чего я раньше не видел. Добавление dist/ к оператору импорта означает, что я обращаюсь к модулям через относительный путь. Я хочу my-lib/Button.

Я использую роллап. Я пытался использовать плагин alias, но это не сработало. Ниже приведена конфигурация моего накопительного пакета:

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,
    }),
  ],
};

А это файл tsconfig:

{
  "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"]
}

Я не знаю, как добиться такой же структуры, как lodash/xxx или material-ui/yyy, с помощью свертки.

Люди предлагают псевдонимы или именованный экспорт, но я не мог заставить это работать.

Самый близкий к моей проблеме вопрос ниже:

Импорт из подпапки пакета npm

Я хочу добиться того же, но с typescript и rollup.

Я думаю, что я что-то упускаю, спасибо.


person alix    schedule 22.06.2020    source источник


Ответы (3)


Во-первых, единственная разница между

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

и

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

это просто еще один уровень каталога.

Как только вы сказали это, пока у вас нет "outDir": "dist", в вашем tsconfig.json файле, вам нужно добавить dist/ к вашим import операторам.

Действительно, обе взятые вами в качестве примера библиотеки распространяются с файлами в корневом каталоге: lodash напрямую имеет js файлов в корне, а material-ui не имеет outDir опция в своем файле tsconfig.json (что означает запись выходных файлов в корневой каталог).

Надеюсь это поможет.

person Daniele Ricci    schedule 26.06.2020
comment
Позвольте мне убедиться, правильно ли я понимаю: когда я установил свою библиотеку в другой проект, весь процесс импорта опирается на относительные пути? Нет никакой магии за кадром из-за бандлеров? Как насчет таких пакетов, как - github.com/rollup/plugins/tree/master/ пакеты/псевдоним или - github.com/tleunen/babel-plugin-module -resolver Я думал, что эти пакеты обеспечивают эту функциональность без перемещения файлов на корневой уровень. - person alix; 26.06.2020
comment
Нет, за кулисами нет никакой магии @alix. Между import {} from "./path" и import {} from "path" есть некоторые различия; Я предположил, что вы знаете о них. Я не мастер этих двух пакетов, но кажется, что они добавляют немного магии для импорта локальной библиотеки со второй формой import. Чтобы уточнить: как ваша библиотека (my-lib) устанавливается на другой проект? Это через npm или еще какой кастомный способ? - person Daniele Ricci; 26.06.2020
comment
Да, он будет установлен через npm. Это не первый мой пакет npm. Я знаком с процессом, но это первый раз, когда я пытался реализовать import from subfolder вещь. Вот почему я в замешательстве. Я не думал, что я перемещаю связанные файлы в корневой каталог и бинго - person alix; 26.06.2020
comment
Да @alix, вот и все. Итак, если вы используете накопитель только для этой конкретной цели, возможно, он вам больше не нужен. - person Daniele Ricci; 26.06.2020
comment
Я понимаю. Позвольте мне попробовать ваш путь, и я вернусь и выпущу награду. Грейзи! - person alix; 26.06.2020
comment
ХОРОШО. Вы правы, я установил пакеты lodash и material-ui и проверил их под node_modules. Они помещают каждый подмодуль на корневой уровень. Вот что я собираюсь сделать. Чтобы сохранить репозиторий моей библиотеки в чистоте, я создам сценарий post-install, чтобы эти связанные файлы перемещались на корневой уровень только тогда, когда пользователь устанавливает мой пакет. Спасибо еще раз! - person alix; 26.06.2020
comment
Я не могу выпустить награду за награду в течение следующих 3 часов. Нам придется подождать. - person alix; 26.06.2020
comment
Мы можем подождать 3 часа :) спасибо. Рад, что был полезен! - person Daniele Ricci; 26.06.2020

Это возможно, но требует некоторых дополнительных действий. Как упоминалось выше, это подход, используемый Material-UI.

Хитрость заключается в том, чтобы опубликовать курируемую папку dist, а не корневую папку вашего репозитория.

Строительство

Для начала давайте просто проясним, что не имеет значения, создана ли ваша библиотека с использованием CommonJS или ESM. Речь идет о разрешении модуля.

Предположим, проект называется my-package.

Теперь большинство проектов после того, как мы построим с src/ по dist/, будут иметь

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

и в package.json

"main": "dist/index.js"

или для ЭСМ

"module": "dist/index.js"

Издательский

Большинство проектов просто добавляют .npmignore и публикуют корень проекта, поэтому при установке проект заканчивается node_modules следующим образом:

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

Разрешение

После установки рассмотрим этот импорт:

import myProject from "my-project";

Это сделает распознаватель модулей (значительно упрощая, так как полный алгоритм здесь неуместен):

  • Go to node_modules
  • Найдите my-project
  • Загрузить package.json
  • Вернуть файл в main или module

Который будет работать, потому что у нас есть

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

Разрешение подпутей

import something from "my-project/something";

Алгоритм разрешения будет работать с

node_modules
  my-project/
    somthing.js

также с

node_modules
  my-project/
    something/
      index.js

и с

node_modules
  my-project/
    something/
      package.json

где в последнем случае он снова будет смотреть на main или module.

Но у нас есть:

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

Хитрость

Хитрость заключается в том, чтобы вместо публикации корня вашего проекта с его папкой dist, открыть папку dist и опубликовать папку dist, используя вместо этого npm publish dist.

По откровенному (как в откровенном письме) вам нужно создать package.json в вашей папке dist; добавить README.md LICENSE и т. д.

Довольно короткий пример того, как это делается, можно найти здесь.

Итак, учитывая, что у нас было после сборки:

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

После публикации мы получаем

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

Где package.json — куратор.

person Izhaki    schedule 08.03.2021

После многочисленных проб и ошибок я смог заставить это работать, передав список входных данных, используя saveModules и preserveModulesRoot, а также простой скрипт после установки.

Вот мой rollup.config.js

const options = {
  input: [
    'src/index.ts',
    'src/api/index.ts',
    'src/components/index.ts',
    'src/contexts/index.ts',
    'src/hooks/index.ts',
    'src/lib/index.ts',
    'src/types/index.ts',
    'src/utils/index.ts',
    'src/UI/index.ts',
  ],
  output: [
    {
      format: 'cjs',
      dir: 'dist',
      exports: 'auto',
      preserveModules: true,
      preserveModulesRoot: 'src',
      sourcemap: true,
    },
  ],
  plugins: [
    // Preferably set as first plugin.
    peerDepsExternal(),
    typescript({
      tsconfig: './tsconfig.rollup.json',
    }),
    postcss({
      extract: false,
      modules: true,
      use: ['sass'],
    }),
  ],
};

export default options;

скрипты/postinstall.sh

#!/usr/bin/env bash
set -e;

# skip postinstall if npm install for development
# rollup.config.js is not included in dist
if [ -f "rollup.config.js" ]; then
  echo "skipping your package's postinstall routine.";
  exit 0;
fi

echo 'Copying files from dist folder into root project folder...'
cp -r dist/* ./ && rm -rf dist
echo 'Postinstall done!'

package.json

"scripts": {
    "postinstall": "./scripts/postinstall.sh",
  },

Это скомпилирует и выведет все файлы в папку dist. Постустановочный скрипт скопирует все файлы из dist в корневую папку проекта.

Примечание*. Сценарий постустановки следует пропустить при локальном запуске npm install. Это делается путем проверки существования файла rollup.config.js.

person TrieuNomad    schedule 06.12.2020