在 10 分钟内构建您的第一个 Typescript 包 NPM 注册表我们正在构建的内容设置文件结构切换组件样式组件属性测试发布后续步骤

2025-06-07

10 分钟内构建您的第一个 Typescript 包

NPM 注册表

我们正在构建什么

设置

文件结构

切换组件

造型

组件道具

测试

发布

后续步骤

我已经推迟构建自己的 Typescript 包一段时间了。

不是因为缺乏想法,而是我知道现代的 Javascript/Typescript 开发一团糟。启动一个默认的 React 项目后,检查一下 node_modules 目录的大小,就能明白我的意思了:仅仅启动一个项目,依赖项就超过 200MB!或者更好的办法是,尝试启动一个没有依赖项的 React 项目create-react-app

我需要花几天时间才能用 Babel、Prettier、Rollup、ESLint、Jest 等工具按照我想要的方式配置我自己的 Typescript 包。更不用说这可能会让我失去理智。

替代文本

然后我偶然发现了TSDX

阅读完README,我能够在一个晚上发布一个包含测试的完整 npm 包。

本指南是我发布第一个软件包时所学内容的简化版本。完成本教程后,你应该会在NPM 仓库中拥有一个已发布并测试过的 Typescript 软件包。

NPM 注册表

首先,您需要创建一个 NPM 帐户并将其配置为在命令行中使用。如果您还没有配置帐户并通过命令行登录,请先阅读本简短指南。npm login

我们正在构建什么

由于本教程面向初学者,因此我们将构建一些简单的东西。一个可复用的 React 组件,包含 Jest 测试、类型和 Github 操作:

替代文本

我知道,这确实令人敬畏。

现场演示

最终源代码

设置

让我们从命令行引导我们的 TSDX 项目:

npx tsdx create toggle
Enter fullscreen mode Exit fullscreen mode

在提示符下,选择 React:
替代文本

安装依赖项后,让我们确保我们可以在开发/监视模式下启动项目:

cd toggle
npm start
Enter fullscreen mode Exit fullscreen mode

您现在应该有一个可以运行的包!

文件结构

> tree -L 2 -I node_modules
.
├── LICENSE
├── README.md
├── dist
│   ├── index.d.ts
│   ├── index.js
│   ├── toggle.cjs.development.js
│   ├── toggle.cjs.development.js.map
│   ├── toggle.cjs.production.min.js
│   ├── toggle.cjs.production.min.js.map
│   ├── toggle.esm.js
│   └── toggle.esm.js.map
├── example
│   ├── index.html
│   ├── index.tsx
│   ├── package.json
│   └── tsconfig.json
├── package-lock.json
├── package.json
├── src
│   └── index.tsx
├── test
│   └── blah.test.tsx
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

默认项目非常精简。不过,有一些目录/文件需要了解。

目录

  • src:这是所有将要实时构建的源文件的位置
  • 示例:用于测试组件/包的示例游乐场
  • dist:将被构建并发布到 npm 的内容。您实际上不需要修改此目录,并且它应该从源代码管理中排除。
  • 测试:你的测试

文件

  • src/index.tsx:将要构建的主要源文件。这需要导入所有其他源文件
  • package.json:包的依赖项/所有配置
  • example/package.json:你的游乐场的依赖项(这些不会发布到 npm)
  • example/index.tsx:为游乐场加载包的文件
  • test/blah.test.tsx:示例测试文件
  • README.md:生成的 README 包含大量有用的信息以供参考。

切换组件

为了遵循 React 最佳实践,我们将为我们的组件创建一个单独的文件。

将以下代码复制并粘贴到src/Toggle.tsx

// Inside src/Toggle.tsx
import React, { FC } from 'react';

export const Toggle: FC = () => {
  return (
    <label className="switch">
      <input type="checkbox" />
      <span className="slider round"></span>
    </label>
  );
};
Enter fullscreen mode Exit fullscreen mode

这里没什么特别的,只是一个默认的 HTML 复选框。让我们从我们的文件中导出组件index.tsx,该文件是包中将要使用的主文件。

// src/index.tsx

export * from './Toggle';
Enter fullscreen mode Exit fullscreen mode

TSDX 项目附带一个示例文件夹,可帮助您在浏览器中可视化组件。我们也将使用它作为组件的沙箱。由于我们更改了组件的名称,因此必须更新示例导入:

// example/index.tsx

import 'react-app-polyfill/ie11';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Toggle } from '../src/index'; // 👈 Change our import 

const App = () => {
  return (
    <div>
      <Toggle />{/* 👈 Change to use your new component*/}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

现在让我们运行这个沙盒环境来看看我们有什么:

cd example
npm i
npm start
Enter fullscreen mode Exit fullscreen mode

导航到http://localhost:1234。您应该会看到一个复选框!

替代文本

造型

现在让我们为这个可爱的复选框添加一些样式。Toggle.css在 src 目录中打开一个名为 的新文件,并将以下样式复制到其中:

/* src/Toggle.css */

/* The switch - the box around the slider */
.switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}

/* Hide default HTML checkbox */
.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

/* The slider */
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .slider {
  background-color: #2196F3;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(26px);
  -ms-transform: translateX(26px);
  transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}
Enter fullscreen mode Exit fullscreen mode

让我们将这些样式导入到我们的Toggle.tsx组件中。我们需要安装来rollup-plugin-postcss告诉 Rollup 如何将 CSS 编译为我们包的一部分:

npm i -D rollup-plugin-postcss
Enter fullscreen mode Exit fullscreen mode

现在在项目根目录中创建一个名为 tsdx.config.js 的文件并粘贴以下代码:

// tsdx.config.js

const postcss = require('rollup-plugin-postcss');

module.exports = {
  rollup(config, options) {
    config.plugins.push(
      postcss({
        plugins: [],
      })
    );
    return config;
  },
};

Enter fullscreen mode Exit fullscreen mode

现在我们可以使用 ESM 导入来导入我们的样式:

// src/Toggle.tsx

import React, { FC } from 'react';
import './Toggle.css'; // 👈 Import our new styles

export const Toggle: FC = () => {
  return (
    <label className="switch">
      <input type="checkbox" />
      <span className="slider round"></span>
    </label>
  );
};

Enter fullscreen mode Exit fullscreen mode

保存并刷新您的浏览器。

替代文本

哒哒!

组件道具

但是如果我们真的想对切换组件的状态做些什么呢?它本身就没什么用。

让我们添加组件道具以便给我们更多的灵活性:

// src/Toggle.tsx

import React, { FC } from 'react';
require('./Toggle.css');

export type ToggleType = {
  isOn: boolean;
  handleChange: () => void;
};

export const Toggle: FC<ToggleType> = ({ isOn, handleChange }) => {
  return (
    <label className="switch">
      <input checked={isOn} onChange={handleChange} type="checkbox" />
      <span className="slider round"></span>
    </label>
  );
};

Enter fullscreen mode Exit fullscreen mode

现在我们可以将 props 传递给组件并管理其状态。由于我们正在导出,我们的类型将自动构建并作为项目的一部分包含进来ToggleType

让我们更新我们的游乐场以包含此状态并确保切换仍然有效:

// example/index.tsx

import 'react-app-polyfill/ie11';
import React, { useState } from 'react';
import * as ReactDOM from 'react-dom';
import { Toggle } from '../src/index';

const App = () => {
  const [isOn, setIsOn] = useState(false);

  return (
    <div>
      <Toggle isOn={isOn} handleChange={() => setIsOn(!isOn)} />
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

Enter fullscreen mode Exit fullscreen mode

现在我们在组件外部setIsOn(!isOn)处理状态。这意味着我们只需调用 ,就可以在任何地方更改切换状态。

测试

我们已经准备好发布我们的软件包了,但首先需要确保它能正常运行。我们希望大家能为您的项目做出贡献,但我们不想每次提交新的 PR 时都在我们的沙盒中测试功能。

让我们将blah.test.tsx文件重命名为toggle.test.tsx并更新我们的react-dom渲染方法:

// src/tests/blah.test.tsx -> src/tests/toggle.test.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Toggle } from '../src';

describe('it', () => {
  it('renders without crashing', () => {
    const div = document.createElement('div');
    ReactDOM.render(<Toggle isOn={false} handleChange={() => {}} />, div);
    ReactDOM.unmountComponentAtNode(div);
  });
});
Enter fullscreen mode Exit fullscreen mode

为了让 Jest 能够读取 CSS 文件,我们需要安装一个包来允许我们模拟这些文件:

npm i -D identity-obj-proxy
Enter fullscreen mode Exit fullscreen mode

然后编辑我们的 package.json 来反映这一点:

// package.json
...
  "jest": {
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less|scss|sass)$": "identity-obj-proxy"
    }
  },
...
Enter fullscreen mode Exit fullscreen mode

请参阅Jest 文档,了解更多关于此操作的必要性。现在,我们应该可以从根目录进行功能测试:

npm test
Enter fullscreen mode Exit fullscreen mode

好极了!

替代文本

唯一的问题是,这只是测试组件是否挂载,并且挂载过程中不会破坏应用。我们真正想测试的是 toggle 功能和isOnprop 是否正常工作。

我们可以使用react-testing-library来测试我们的组件 prop 功能:

npm i -D @testing-library/react @testing-library/jest-dom
Enter fullscreen mode Exit fullscreen mode

让我们更新测试文件以使用一些新的测试方法。我们将使用renderfireEvent方法:

// test/toggle.test.tsx

import * as React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { Toggle } from '../src';

it('Should render the toggle and be clickable between states', () => {
  // mock onChange function
  const onChange = jest.fn();

  const { getByTestId, rerender } = render(
    <Toggle isOn={false} handleChange={onChange} />
  );
  // checkbox and parent label components
  const checkbox = getByTestId('Toggle');
  const label = getByTestId('Toggle-label');

  // isOn=false should mean it's unchecked
  expect(checkbox).toHaveProperty('checked', false);

  // Clicking from off -> on
  fireEvent.click(label);
  expect(onChange).toHaveBeenCalledTimes(1);

  // isOn=true should mean it's checked
  rerender(<Toggle isOn={true} handleChange={onChange} />);
  expect(checkbox).toHaveProperty('checked', true);

  // Clicking from on -> off
  fireEvent.click(label);
  expect(onChange).toHaveBeenCalledTimes(2);
});
Enter fullscreen mode Exit fullscreen mode

如果你觉得有点困惑,或者你不熟悉 react-testing-library,没关系。我们实际上做的就是渲染组件,并确保它isOn反映选中状态,并且handleChange每次点击时都会调用我们的函数。

再次检查它是否仍然有效:

npm test
Enter fullscreen mode Exit fullscreen mode

发布

您需要确保更新软件包的版本、作者和名称。名称应唯一,并且不会被 NPM 注册表占用。您需要在 package.json 中更改三个字段:

 "author": "Frodo Baggins",
 "name: "frodo-toggle",
 "version": "1.0.0",
Enter fullscreen mode Exit fullscreen mode

最后一步就是现在发布!

npm publish
Enter fullscreen mode Exit fullscreen mode

如果出现错误,您可能需要 a) 通过 重新登录,或 b) 更改包名称以使其唯一。如果您想查看该包名称是否可用,请尝试在npm 注册表npm login中搜索

恭喜,您现在是一名 Typescript 包作者。😎

现在任何人都可以通过运行以下命令从命令行安装您的软件包:

npm i your-toggle-lib # replace this with your package name
Enter fullscreen mode Exit fullscreen mode

后续步骤

您可以从现在开始做一些事情来改进这个包。如果您计划允许外部贡献者使用,您可能需要调整 TSDX 自带的默认Github 操作,以便在新的 PR 上运行测试套件。这将确保外部贡献者不会合并损坏的更改。

其他可能的后续步骤:

  • 添加道具来改变颜色并向切换按钮添加标签。
  • 添加size具有“小”、“中”和“大”选项的道具。
  • 根据道具添加不同的过渡。
  • 添加styled-components而不是 css

世界是你的牡蛎!

配置是任何项目中最痛苦的部分,但像 TSDX 和 create-react-app 这样的库却能出色地降低新手和懒人(比如我)的入门门槛。没有人喜欢花一整天时间与配置文件作斗争。希望本指南能让你更有信心编写自己的软件包。期待在 Github 和 npm 上看到你的项目!

文章来源:https://dev.to/g_abud/publish-a-typescript-package-in-under-10-minutes-4940
PREV
使用 Apache Airflow 管理数据管道
NEXT
最佳编程挑战网站,助你练习编程技能