Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
Expand Down
17 changes: 17 additions & 0 deletions packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,23 @@ export default function App() {
}
```

### Per-flag subpath imports

Each flag is also exposed as its own module under `./flags/<code>` (ISO code,
lowercase). This guarantees only the imported flag lands in your bundle even
when your bundler can't tree-shake the barrel (e.g. Turbopack as of Next.js 16):

```tsx
import { FlagUs } from '@sankyu/react-circle-flags/flags/us'
import { FlagBr } from '@sankyu/react-circle-flags/flags/br'
```

> [!NOTE]
> Named imports from the package root are equivalent under bundlers with
> whole-barrel tree-shaking (webpack, Rollup, esbuild). Prefer subpaths when
> you need the guarantee, or when measuring shows barrel imports bloating
> your chunks.

### Other usage examples

> [!TIP]
Expand Down
5 changes: 5 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./flags/*": {
"types": "./dist/flags/*.d.ts",
"import": "./dist/flags/*.mjs",
"require": "./dist/flags/*.cjs"
}
},
"files": [
Expand Down
2 changes: 2 additions & 0 deletions packages/react/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
Expand All @@ -15,6 +16,7 @@
"skipLibCheck": true,
"moduleResolution": "bundler",
"baseUrl": ".",
"rootDir": "../..",
"paths": {
"@sankyu/circle-flags-core": ["../core/src"],
"@sankyu/circle-flags-core/*": ["../core/src/*"]
Expand Down
2 changes: 2 additions & 0 deletions packages/solid/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
Expand All @@ -8,6 +9,7 @@
"strict": true,
"moduleResolution": "bundler",
"baseUrl": ".",
"rootDir": "../..",
"paths": {
"@sankyu/circle-flags-core": ["../core/src"],
"@sankyu/circle-flags-core/*": ["../core/src/*"],
Expand Down
2 changes: 2 additions & 0 deletions packages/vue/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
Expand All @@ -14,6 +15,7 @@
"skipLibCheck": true,
"moduleResolution": "bundler",
"baseUrl": ".",
"rootDir": "../..",
"paths": {
"@sankyu/circle-flags-core": ["../core/src"],
"@sankyu/circle-flags-core/*": ["../core/src/*"]
Expand Down
45 changes: 37 additions & 8 deletions scripts/tasks/postbuild-react.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,48 @@ export const postbuildReact = () => {
process.exit(1)
}

// Generate per-flag .d.ts files so the `./flags/*` subpath exports are
// fully typed. tsup's dts rollup produces broken chunks for the per-flag
// entries (see packages/react/tsup.config.ts), so declarations are emitted
// here from the generated sources instead. Any stray tsup-emitted .d.ts is
// overwritten in the process.
try {
const files = readdirSync(flagsDir)
let removedCount = 0
const generatedDir = join(process.cwd(), 'generated/flags')
const sources = readdirSync(generatedDir).filter(f => f.endsWith('.tsx'))
let declared = 0

for (const file of files) {
if (!file.endsWith('.d.ts')) continue
unlinkSync(join(flagsDir, file))
removedCount++
for (const file of sources) {
const code = file.replace(/\.tsx$/, '')
const source = readFileSync(join(generatedDir, file), 'utf-8')

// Alias flags re-export their target: keep the same shape in the .d.ts
// so TypeScript resolves through the sibling declaration.
const alias = source.match(/^export \{ (\w+) as (\w+) \} from '\.\/([\w-]+)'/)

let dts
if (alias) {
dts = `export { ${alias[1]} as ${alias[2]} } from './${alias[3]}'\n`
} else {
const name = source.match(/export const (Flag\w+)/)?.[1]
if (!name) {
throw new Error(`Could not find flag export in generated/flags/${file}`)
}
dts = `import type { ReactElement, SVGProps } from 'react'
import type { FlagComponentProps } from '@sankyu/circle-flags-core'
export declare const ${name}: (
props: SVGProps<SVGSVGElement> & FlagComponentProps
) => ReactElement
`
}

writeFileSync(join(flagsDir, `${code}.d.ts`), dts, 'utf-8')
declared++
}

console.log(`✅ Removed ${removedCount} redundant .d.ts files in dist/flags/`)
console.log(`✅ Generated ${declared} per-flag .d.ts files in dist/flags/`)
} catch (error) {
console.error('⚠️ Failed to cleanup dist/flags/*.d.ts:', error)
console.error('❌ Failed to generate dist/flags/*.d.ts:', error)
process.exit(1)
}

const dctsPath = join(process.cwd(), 'dist/index.d.cts')
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
Expand Down