@teispace/next-maker 5.0.3 → 5.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.js +8 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.0.5](https://github.com/teispace/npm-packages/compare/next-maker-v5.0.4...next-maker-v5.0.5) (2026-09-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **next-maker:** let locale and component run without a terminal ([#170](https://github.com/teispace/npm-packages/issues/170)) ([fa818d9](https://github.com/teispace/npm-packages/commit/fa818d94868e726d389a6cb27ea90fc414a09198))
|
|
9
|
+
|
|
10
|
+
## [5.0.4](https://github.com/teispace/npm-packages/compare/next-maker-v5.0.3...next-maker-v5.0.4) (2026-09-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **next-maker:** keep generated placeholders free of empty object types ([#168](https://github.com/teispace/npm-packages/issues/168)) ([b0b3d40](https://github.com/teispace/npm-packages/commit/b0b3d40f6d9d8e3ea8966143527be58f8bbdf2e1))
|
|
16
|
+
|
|
3
17
|
## [5.0.3](https://github.com/teispace/npm-packages/compare/next-maker-v5.0.2...next-maker-v5.0.3) (2026-09-06)
|
|
4
18
|
|
|
5
19
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as Fp}from"commander";var Ae={name:"@teispace/next-maker",version:"5.0.
|
|
2
|
+
import{Command as Fp}from"commander";var Ae={name:"@teispace/next-maker",version:"5.0.5",description:"Create and grow Next.js 16 applications from the Teispace starter: composable init, feature-aware setup and doctor, and generators for the server-first data layer.",keywords:["nextjs","react","typescript","tailwindcss","starter","template","redux","zustand","tanstack-query","server-actions","cli"],type:"module",homepage:"https://github.com/teispace/npm-packages/tree/main/packages/next-maker#readme",bugs:{url:"https://github.com/teispace/npm-packages/issues"},license:"MIT",author:"Teispace",files:["dist","README.md","LICENSE","CHANGELOG.md"],bin:"dist/index.js",repository:{type:"git",url:"git+https://github.com/teispace/npm-packages.git",directory:"packages/next-maker"},scripts:{build:"tsx build.ts",start:"node dist/index.js",dev:"tsx --watch src/index.ts",test:"vitest run","test:watch":"vitest","test:cov":"vitest run --coverage","type-check":"tsc --noEmit",clean:"rm -rf dist",prepublishOnly:"yarn clean && yarn build",smoke:"tsx scripts/smoke.ts"},dependencies:{commander:"^15.0.0",degit:"^3.8.0",enquirer:"^2.4.1",ora:"^9.4.1",picocolors:"^1.1.1","png-to-ico":"^3.0.2",sharp:"^0.35.3"},devDependencies:{"@types/degit":"^2.8.6","@types/node":"^26.2.0",esbuild:"^0.28.2",tsx:"^4.23.12",typescript:"^6.0.3",vitest:"^4.1.11"},engines:{node:">=24.0.0"},publishConfig:{access:"public",registry:"https://registry.npmjs.org/"}};import{copyFile as ea}from"node:fs/promises";import At from"node:path";import I from"picocolors";var sn=/@next-maker:([\w-]+)(?::(start|end))?/,Ys=/^\s*(?:\{\s*\/\*.*?\*\/\s*\}|\/\*.*?\*\/|\/\/.*|#.*)\s*$/,Qs=e=>e.replace(/\s*\{\s*\/\*[^*]*@next-maker:[^*]*\*\/\s*\}\s*$/,"").replace(/\s*\/\*[^*]*@next-maker:[^*]*\*\/\s*$/,"").replace(/\s*\/\/[^\n]*@next-maker:[^\n]*$/,"").replace(/\s*#[^\n]*@next-maker:[^\n]*$/,""),Xt=(e,{off:t})=>{let r=e.split(`
|
|
3
3
|
`),n=[];for(let o=0;o<r.length;o++){let s=r[o],i=s.match(sn);if(!i){n.push(s);continue}let[,a,c]=i,l=t.has(a);if(c==="start"){let u=Zs(r,o+1,a);l&&(o=u);continue}if(c==="end")continue;let p=Ys.test(s);if(l){p&&o++;continue}p||n.push(Qs(s))}return n.join(`
|
|
4
4
|
`)},Zs=(e,t,r)=>{for(let n=t;n<e.length;n++){let o=e[n].match(sn);if(o&&o[1]===r&&o[2]==="end")return n}throw new Error(`Anchor @next-maker:${r}:start has no matching :end`)},ze=e=>e.includes("@next-maker:"),an=(e,t)=>{let r=e.split(`
|
|
5
5
|
`),n=[],o=new RegExp(`^import\\s+${t}\\s+from\\s+['"][^'"]+['"];?\\s*$`),s=/^(import\s+(?:type\s+)?)\{([^}]+)\}(\s+from\s+['"][^'"]+['"];?\s*)$/;for(let i of r){if(o.test(i))continue;let a=i.match(s);if(a){let c=a[2].split(",").map(p=>p.trim()).filter(Boolean),l=c.filter(p=>p.replace(/^type\s+/,"").split(/\s+as\s+/)[0]!==t);if(l.length===0)continue;if(l.length<c.length){n.push(`${a[1]}{ ${l.join(", ")} }${a[3]}`);continue}}n.push(i)}return n.join(`
|
|
@@ -263,7 +263,8 @@ export interface ${t}State {
|
|
|
263
263
|
error: string | null;
|
|
264
264
|
}
|
|
265
265
|
`:`export interface ${t}Props {
|
|
266
|
-
|
|
266
|
+
/** Extra classes for the root element. Add this feature's own props here. */
|
|
267
|
+
className?: string;
|
|
267
268
|
}
|
|
268
269
|
`};var so=e=>{let{componentName:t,camelName:r,typesImportPath:n}=e;return`import type { StateCreator } from 'zustand';
|
|
269
270
|
|
|
@@ -574,7 +575,8 @@ export default function ${e.componentName}Error({
|
|
|
574
575
|
import { createContext, useContext, useMemo } from 'react';
|
|
575
576
|
|
|
576
577
|
type ${n} = {
|
|
577
|
-
|
|
578
|
+
/** Replace with this provider's own fields. */
|
|
579
|
+
ready: boolean;
|
|
578
580
|
};
|
|
579
581
|
|
|
580
582
|
const ${o} = createContext<${n} | null>(null);
|
|
@@ -588,7 +590,7 @@ export const ${s} = (): ${n} => {
|
|
|
588
590
|
};
|
|
589
591
|
|
|
590
592
|
export const ${t} = ({ children }: { children: React.ReactNode }) => {
|
|
591
|
-
const value = useMemo<${n}>(() => ({}), []);
|
|
593
|
+
const value = useMemo<${n}>(() => ({ ready: true }), []);
|
|
592
594
|
return <${o}.Provider value={value}>{children}</${o}.Provider>;
|
|
593
595
|
};
|
|
594
596
|
`},xo=e=>yo(e,"Provider");var Ba=/^[A-Za-z][A-Za-z0-9-]*$/,Va=e=>e.includes("-")?$(e):e.charAt(0).toUpperCase()+e.slice(1),Tr=async e=>{let{name:t,projectPath:r}=e;if(!Ba.test(t))throw new Error(`Invalid provider name "${t}". Use kebab-case or PascalCase letters and digits only.`);let n=Va(t).replace(/Provider$/,""),o=xo(n),s=o,i=Er.join(r,C.PROVIDERS),a=Er.join(i,`${s}.tsx`);if(w(a))throw new Error(`Provider already exists at ${Er.relative(r,a)}.`);return await Ga(i,{recursive:!0}),await P(a,wo({baseName:n})),{componentName:o,fileBaseName:s,providerFile:a}};import pt from"node:path";var oe=async e=>{let{projectPath:t,sourceFile:r,kind:n,symbolName:o}=e,s=pt.dirname(r),i=pt.basename(r).replace(/\.(tsx?|jsx?)$/i,""),a=n==="component"?".tsx":".ts",c=pt.join(s,`${i}.test${a}`),l=`./${i}`,p=Ha(s,pt.join(t,"test","test-utils")),u;return n==="component"?u=Mt({componentName:o,sourceImportPath:l,testUtilsImportPath:p,hasState:!!e.hasState,hasI18n:!!e.hasI18n}):n==="hook"?u=co({hookName:o,sourceImportPath:l,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?p:void 0}):u=po({camelName:o,sourceImportPath:l}),await P(c,u),c},Ha=(e,t)=>{let r=pt.relative(e,t).replace(/\\/g,"/");return r.startsWith(".")?r:`./${r}`};import{readFile as qa,writeFile as Ja}from"node:fs/promises";import za from"node:path";var Ge=(e,t)=>{let r=/import\s+.*\s+from\s+['"].*['"];?\n/g,n=e.match(r);if(n&&n.length>0){let o=n[n.length-1],s=e.lastIndexOf(o);return e.slice(0,s+o.length)+t+`
|
|
@@ -616,7 +618,7 @@ ${o}</${t}>`;return e.replace(r,i)},Ro=(e,t)=>{let r=`export * from './${t}';`;i
|
|
|
616
618
|
`),o=[],s=[];for(let u of n)/^export \* from '\.\/.*';\s*$/.test(u)?o.push(u):s.push(u);let i=[...o,r].sort(),a=n.findIndex(u=>/^export \* from '\.\//.test(u));if(a===-1)return`${r}
|
|
617
619
|
${e.replace(/^\n+/,"")}`;let c=n.slice(0,a),l=n.findIndex((u,f)=>f>a&&!/^export \* from '\.\//.test(u)),p=l===-1?[]:n.slice(l);return[...c,...i,...p].join(`
|
|
618
620
|
`)},oc=C.PROVIDERS,sc=C.ROOT_PROVIDER,ic=C.PROVIDERS_INDEX,Co=async(e,t={fileExists:w,readFile:R})=>{let r=Lt.join(e,sc);if(t.fileExists(r))return r;let n=Lt.join(e,oc);if(!t.fileExists(n))return null;let{readdir:o}=await import("node:fs/promises"),i=(await o(n).catch(()=>[])).filter(c=>c.endsWith(".tsx")),a=null;for(let c of i){let l=Lt.join(n,c),p=await t.readFile(l);if(!/\{children\}/.test(p))continue;let f=((p.split("{children}")[0]??"").match(/<[A-Z][A-Za-z0-9_]*\b/g)??[]).length;(!a||f>a.depth)&&(a={file:l,depth:f})}return a?.file??null},Fr=async e=>{let{projectPath:t,componentName:r,fileBaseName:n}=e,o=await Co(t);if(o){let a=await R(o),c=a;(!/from '\.\//.test(a)||!a.includes(`{ ${r} }`))&&(c=Ge(c,`import { ${r} } from './${n}';`)),c=jo(c,r),c!==a&&await P(o,c)}let s=Lt.join(t,ic),i=null;if(w(s)){let a=await R(s),c=Ro(a,n);c!==a&&await P(s,c),i=s}return{rootProviderFile:o,barrelFile:i}};import{readFile as Oo,writeFile as Ao}from"node:fs/promises";import Io from"node:path";var mt=async e=>{let{projectPath:t,name:r,persist:n,importPath:o}=e,s=Io.join(t,"src","store","rootReducer.ts"),i=Io.join(t,"src","store","index.ts"),a=G(r),c=o.replace(/^src\//,"@/");try{let l=await Oo(s,"utf-8");if(l=Ge(l,`import { ${a}Slice } from '${c}';`),l=jr(l,`${a}Slice`),await Ao(s,l),n&&w(i)){let p=await Oo(i,"utf-8");p=Ge(p,`import { ${a}Persistence } from '${c}';`),p=Rr(p,`${a}Persistence`),await Ao(i,p)}}catch(l){throw new Error(`Failed to register slice '${r}': ${l}`,{cause:l})}};import{readFile as ac,writeFile as cc}from"node:fs/promises";import pc from"node:path";var Te=async(e,t)=>{let r=pc.join(e,"src","i18n","translations","en.json");if(!w(r))return;let n=await ac(r,"utf-8"),o=JSON.parse(n);o[t]||(o[t]={title:t,description:`${t} page description`},await cc(r,`${JSON.stringify(o,null,2)}
|
|
619
|
-
`))};import lc from"enquirer";var{prompt:mc}=lc,No=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"componentName",message:"Component name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Component name is required"}),t.client===void 0&&r.push({type:"confirm",name:"isClient",message:"Add 'use client' directive?",initial:!1});let n=r.length>0?await mc(r):{};return{componentName:e??n.componentName,isClient:t.client??n.isClient??!1}};var Fo=e=>{e.command("component [name]").description("Generate a shared component").option("--client","Add 'use client' directive").option("--i18n","Add useTranslations hook").option("--feature <path>","Generate in feature directory (e.g., src/features/auth)").option("--test","Also generate a sibling test file").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(X.cyan(`
|
|
621
|
+
`))};import lc from"enquirer";var{prompt:mc}=lc,No=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"componentName",message:"Component name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Component name is required"}),t.client===void 0&&r.push({type:"confirm",name:"isClient",message:"Add 'use client' directive?",initial:!1});let n=r.length>0?await mc(r):{};return{componentName:e??n.componentName,isClient:t.client??n.isClient??!1}};var Fo=e=>{e.command("component [name]").description("Generate a shared component").option("--client","Add 'use client' directive").option("--no-client","Keep the component a Server Component").option("--i18n","Add useTranslations hook").option("--feature <path>","Generate in feature directory (e.g., src/features/auth)").option("--test","Also generate a sibling test file").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(X.cyan(`
|
|
620
622
|
\u{1F9E9} Component Generator
|
|
621
623
|
`)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected");let i=await No(t,{client:r.client});M(i.componentName,"component name"),r.feature&&W(r.feature,"feature path");let a=$(i.componentName),c=i.isClient,l=r.i18n??!1,p=uc(r,s.hasTests);h.start("Generating component..."),await fr({name:i.componentName,projectPath:n,isClient:c||l,hasI18n:l,featurePath:r.feature}),h.succeed("Component generated"),l&&await Te(n,a);let u=null;if(p){let d=r.feature?Mr.join(n,r.feature,"components",`${a}.tsx`):Mr.join(n,"src/components/common",a,`${a}.tsx`);h.start("Generating test..."),u=await oe({projectPath:n,sourceFile:d,kind:"component",symbolName:a,hasState:s.state!=="none",hasI18n:s.hasI18n}),h.succeed("Test generated")}let f=r.feature?`${r.feature}/components/${a}.tsx`:`src/components/common/${a}/${a}.tsx`;await k(n,o),m(X.green(`
|
|
622
624
|
\u2728 Component '${a}' created successfully!
|
|
@@ -653,7 +655,7 @@ ${e.map(r=>` { src: '${r.src}', sizes: '${r.sizes}', type: '${r.type}'${r.pur
|
|
|
653
655
|
`)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected");let i=s.hasI18n&&r.locale!==!1;m(Vt.dim(` i18n: ${i?"\u2713":"\u2717"}
|
|
654
656
|
`)),h.start("Generating layout...");let a=await vr({segment:t,at:r.at,projectPath:n,hasI18n:i,withGroup:!!r.group});h.succeed("Layout generated"),await k(n,o),m(Vt.green(`
|
|
655
657
|
\u2728 ${a.componentName} created!
|
|
656
|
-
`)),m(Vt.dim(` \u{1F4C4} ${a.displayPath}`)),m("")}catch(n){h.fail("Layout generation failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Q from"picocolors";import zc from"enquirer";var{prompt:Wc}=zc,cs=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"code",message:"Locale code (e.g., es, fr, de):",validate:o=>o?/^[a-z]{2}(-[A-Z]{2})?$/.test(o)?!0:"Use format: xx or xx-XX (e.g., es, pt-BR)":"Locale code is required"}),t.name||r.push({type:"input",name:"name",message:"Language name (e.g., Spanish, French):",validate:o=>o?!0:"Language name is required"}),t.country||r.push({type:"input",name:"country",message:"Country (e.g., Spain, France):",validate:o=>o?!0:"Country is required"}),t.flag||r.push({type:"input",name:"flag",message:"Flag emoji (e.g., \u{1F1EA}\u{1F1F8}, \u{1F1EB}\u{1F1F7}):",validate:o=>o?!0:"Flag emoji is required"}),t.copyTranslations===void 0&&r.push({type:"confirm",name:"copyTranslations",message:"Copy translations from English? (No = empty values)",initial:!1});let n=r.length>0?await Wc(r):{};return{code:e??n.code,name:t.name??n.name,country:t.country??n.country,flag:t.flag??n.flag,copyTranslations:t.copyTranslations??n.copyTranslations??!1}};var ps=e=>{e.command("locale [code]").description("Add a new locale/language").option("--copy-translations","Copy translations from English instead of empty values").option("--name <name>","Language name, e.g. Spanish").option("--country <country>","Country, e.g. Spain").option("--flag <emoji>","Flag emoji, e.g. \u{1F1EA}\u{1F1F8}").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(Q.cyan(`
|
|
658
|
+
`)),m(Vt.dim(` \u{1F4C4} ${a.displayPath}`)),m("")}catch(n){h.fail("Layout generation failed"),b(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Q from"picocolors";import zc from"enquirer";var{prompt:Wc}=zc,cs=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"code",message:"Locale code (e.g., es, fr, de):",validate:o=>o?/^[a-z]{2}(-[A-Z]{2})?$/.test(o)?!0:"Use format: xx or xx-XX (e.g., es, pt-BR)":"Locale code is required"}),t.name||r.push({type:"input",name:"name",message:"Language name (e.g., Spanish, French):",validate:o=>o?!0:"Language name is required"}),t.country||r.push({type:"input",name:"country",message:"Country (e.g., Spain, France):",validate:o=>o?!0:"Country is required"}),t.flag||r.push({type:"input",name:"flag",message:"Flag emoji (e.g., \u{1F1EA}\u{1F1F8}, \u{1F1EB}\u{1F1F7}):",validate:o=>o?!0:"Flag emoji is required"}),t.copyTranslations===void 0&&r.push({type:"confirm",name:"copyTranslations",message:"Copy translations from English? (No = empty values)",initial:!1});let n=r.length>0?await Wc(r):{};return{code:e??n.code,name:t.name??n.name,country:t.country??n.country,flag:t.flag??n.flag,copyTranslations:t.copyTranslations??n.copyTranslations??!1}};var ps=e=>{e.command("locale [code]").description("Add a new locale/language").option("--copy-translations","Copy translations from English instead of empty values").option("--no-copy-translations","Start the new locale with empty values").option("--name <name>","Language name, e.g. Spanish").option("--country <country>","Country, e.g. Spain").option("--flag <emoji>","Flag emoji, e.g. \u{1F1EA}\u{1F1F8}").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(Q.cyan(`
|
|
657
659
|
\u{1F30D} Locale Generator
|
|
658
660
|
`)),h.start("Detecting project setup...");let s=await O(n);h.succeed("Project setup detected"),s.hasI18n||(h.fail("i18n is not set up in this project"),b("Please set up internationalization first"),m(Q.dim(`
|
|
659
661
|
Run: npx @teispace/next-maker setup --i18n
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teispace/next-maker",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.5",
|
|
4
4
|
"description": "Create and grow Next.js 16 applications from the Teispace starter: composable init, feature-aware setup and doctor, and generators for the server-first data layer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|