ค้นหาเว็บไซต์

svg - ทำไม SVGR ถึงไม่สร้างไฟล์ประกาศ TypeScript ในการตั้งค่า esbuild ของฉัน?


ฉันกำลังสร้างไลบรารีไอคอน SVG ใน TypeScript จนถึงตอนนี้ SVGR ทำงานได้ยอดเยี่ยม แต่สิ่งสุดท้ายที่ฉันต้องการคือประเภทที่สร้างขึ้นเพื่อให้สามารถส่ง title และ ref ไปยังคอมโพเนนต์ที่รวมอยู่ได้

การสืบพันธุ์

ฉันไม่แน่ใจว่าทุกอย่างที่อาจมีประโยชน์ในการดูคืออะไร ดังนั้นฉันจะรวมรีโปและตัวอย่างโค้ดบางส่วน แต่ด้วยการตั้งค่าต่อไปนี้ svgr กำลังสร้างคอมโพเนนต์จากการนำเข้า SVG แต่ไม่มีการสร้างประเภทใด ๆ เป็นผลให้ทุกครั้งที่ฉันพยายามใช้ไอคอนจากแพ็กเกจนี้ ฉันจะได้รับคำเตือนว่าค้นหาไฟล์ประกาศสำหรับมันไม่พบ

ที่เก็บ POC: https://github.com/yuschick/icon-demo

src/index.ts

export { default as TestIcon } from "./svg/test.svg";
export { default as ArrowThinLeft } from './svg/arrow-thin-left.svg';

package.json

{
  "module": "dist/index.js",
  "types": "dist/index.d.ts",
  "type": "module",
  "scripts": {
    "build": "node esbuild.config.js",
  },
  "devDependencies": {
    "esbuild": "^0.14.39",
    "esbuild-plugin-svgr": "^1.0.1",
    "typescript": "^4.6.3"
  }
}

esbuild.config.js

import esbuild from "esbuild";
import svgr from "esbuild-plugin-svgr";

esbuild.build({
    bundle: true,
    entryPoints: ["src/index.ts"],
    external: ["react"],
    format: "esm",
    // minify: true,
    outfile: "dist/index.js",
    plugins: [svgr({ titleProp: true, ref: true, expandProps: false, typescript: true })],
})
  .catch(() => process.exit(1));

tsconfig.json

{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "alwaysStrict": true,
    "baseUrl": "src",
    "checkJs": true,
    "declaration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "outDir": "dist",
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "esnext"
  },
  "include": ["src", "global.d.ts"]
}

global.d.ts

declare module '*.svg' {
    const content: string;
    export default content;
}

พฤติกรรมที่คาดหวัง

โดยการส่งค่า typescript: true เข้าไปในตัวเลือกของ svgr ผมคาดหวังว่าจะมีการสร้างไฟล์ index.d.tsx ขึ้นตามคอมโพเนนต์ที่สร้างขึ้น

ตามเอกสารของ SVGR:

สร้างไฟล์ .tsx พร้อมการพิมพ์ประเภทของ TypeScript

หมายเหตุ: ในขณะที่ฉันกำลังใช้ SVGR ร่วมกับ ESBuild ปัญหาเดียวกันก็เกิดขึ้นเมื่อพยายามใช้กับ Rollup ด้วยเช่นกัน

คำตอบ #1

ปัญหา

โดยการส่งค่า typescript: true เข้าไปในตัวเลือกของ svgr ผมคาดหวังว่าจะมีการสร้างไฟล์ index.d.tsx ขึ้นตามคอมโพเนนต์ที่สร้างขึ้น

ความคาดหวังนี้ไม่ถูกต้อง SVGR จะสร้างไฟล์ .tsx เท่านั้นหากคุณระบุตัวเลือก typescript และไม่ใช่ไฟล์ .d.ts จากเอกสารประกอบ (ซึ่งคุณก็พบแล้วเช่นกัน):

TypeScript

สร้างไฟล์ .tsx พร้อมกับการกำหนดชนิดข้อมูล TypeScript

อย่างไรก็ตาม แม้จะระบุทางเลือกนี้แล้ว SVGR ก็จะไม่สร้างไฟล์ .tsx เพราะคุณกำลังใช้สิ่งนี้ภายในปลั๊กอิน esbuild esbuild-plugin-svgr ในพื้นหลังจะใช้ฟังก์ชัน transform จาก @svgr/core (ดู esbuild-plugin-svgr/index.js:L4) ฟังก์ชัน transform จะเปลี่ยน SVG เป็น JSX หรือ TSX เท่านั้น แต่จะไม่เขียนผลลัพธ์ @svgr/cli จัดการเขียนผลลัพธ์ไปยังไฟล์ เช่น ดู @svgr/cli/src/dirCommand.ts.

วิธีแก้ปัญหา

ฉันคิดว่าคุณมีตัวเลือกสองอย่าง:

แยก SVGR ออกจาก esbuild จากนั้นสร้างไฟล์ .d.ts

ในคำอื่น ๆ ให้หยุดใช้ esbuild-plugin-svgr และใช้ @svgr/cli แทน จากนั้นคุณจะต้องรัน SVGR ก่อน esbuild; ไม่ว่าจะทำด้วยตนเอง, เชื่อมต่อคำสั่งในสคริปต์ หรือใช้ pre-script hook ของ npm หลังจากนั้นคุณจะต้องสร้าง .d.ts file แต่เมื่อดูที่ esbuild#95 ดูเหมือนไม่รองรับโดยตรง คุณอาจต้องการสำรวจข้อเสนอแนะที่ระบุไว้ที่นั่นรวมถึง:

  • การใช้คอมไพเลอร์ TypeScript เพื่อสร้างไฟล์ประกาศ (tsc --emitDeclarationOnly)
  • การใช้ปลั๊กอิน เช่น esbuild-plugin-d.ts

โซ่การสร้างของคุณอาจดูเหมือนแบบนี้ในตอนท้าย:

  1. สร้างไฟล์ .tsx จาก SVG โดยใช้ @svgr/cli
  2. รวมแพ็กเกจโดยใช้ esbuild
  3. สร้างไฟล์ .d.ts ได้ ไม่ว่าจะเป็นขั้นตอนแยกโดยใช้ tsc หรือผสานกับขั้นตอนข้างต้นโดยใช้ปลั๊กอินของ esbuild

สร้างไฟล์ .d.ts จากชุดไฟล์ JavaScript ของคุณ

ถ้าด้วยเหตุผลบางอย่างคุณต้องการเก็บการตั้งค่าการสร้างปัจจุบันของคุณจริงๆ คุณสามารถสร้างไฟล์ .d.ts จากไฟล์ JavaScript bundle ของคุณด้วย tsc dist/index.js --declaration --allowJs --emitDeclarationOnly (ดูเอกสาร TypeScript) ข้อควรระวังคือ TypeScript อาจไม่สามารถสรุปประเภททั้งหมดได้ ดังนั้นคุณอาจมีไฟล์ประกาศประเภทที่ไม่สมบูรณ์

คำตอบ #2

เคยมีปัญาคล้ายกันกับ svgr ตอนพยายามรวมประเภทของ TypeScript ด้วยปลั๊กอิน rollup-plugin-dts

แก้ไขโดยการสร้างไฟล์ icon.d.ts ไฟล์เดียว:

import { SVGProps } from 'react';
declare const ReactComponent: (props: SVGProps<SVGSVGElement>) => JSX.Element;
export { ReactComponent };

และจากนั้นใช้ปลั๊กอิน alias ดังนี้:

import alias from '@rollup/plugin-alias';
...
      plugins: [
        alias({
          entries: [
            {
              find: /^.*\.svg$/,
              replacement: 'src/types/icon.d.ts',
            },
          ],
        }),
        dts(),
      ],