create-plasmic-app 0.0.167 → 0.0.169

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.
Files changed (122) hide show
  1. package/README.internal.md +6 -4
  2. package/cpa-out/gatsby-codegen-js/package.json +3 -2
  3. package/cpa-out/gatsby-codegen-js/plasmic.json +5 -8
  4. package/cpa-out/gatsby-codegen-js/src/components/plasmic/create_plasmic_app/PlasmicButton.jsx +12 -12
  5. package/cpa-out/gatsby-codegen-js/src/components/plasmic/create_plasmic_app/PlasmicDynamicPage.jsx +18 -18
  6. package/cpa-out/gatsby-codegen-js/src/components/plasmic/create_plasmic_app/PlasmicHomepage.jsx +12 -16
  7. package/cpa-out/gatsby-codegen-js/src/components/plasmic/create_plasmic_app/PlasmicRandomDynamicPageButton.jsx +1 -0
  8. package/cpa-out/gatsby-codegen-js/src/components/plasmic/create_plasmic_app/PlasmicStyleTokensProvider.jsx +2 -2
  9. package/cpa-out/gatsby-codegen-js/src/components/plasmic/create_plasmic_app/{plasmic.module.css → plasmic.css} +89 -73
  10. package/cpa-out/gatsby-codegen-js/src/components/plasmic/{plasmic__default_style.module.css → plasmic__default_style.css} +16 -0
  11. package/cpa-out/gatsby-codegen-ts/package.json +3 -2
  12. package/cpa-out/gatsby-codegen-ts/plasmic.json +5 -8
  13. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicButton.tsx +12 -12
  14. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicDynamicPage.tsx +18 -18
  15. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicHomepage.tsx +12 -16
  16. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicRandomDynamicPageButton.tsx +1 -1
  17. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicStyleTokensProvider.tsx +2 -2
  18. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/create_plasmic_app/{plasmic.module.css → plasmic.css} +89 -73
  19. package/cpa-out/gatsby-codegen-ts/src/components/plasmic/{plasmic__default_style.module.css → plasmic__default_style.css} +16 -0
  20. package/cpa-out/gatsby-loader-js/package.json +1 -1
  21. package/cpa-out/gatsby-loader-ts/package.json +1 -1
  22. package/cpa-out/nextjs-app-codegen-js/app/dynamic/[slug]/page.jsx +5 -5
  23. package/cpa-out/nextjs-app-codegen-js/app/robots.js +24 -0
  24. package/cpa-out/nextjs-app-codegen-js/app/sitemap.js +38 -0
  25. package/cpa-out/nextjs-app-codegen-js/components/plasmic/create_plasmic_app/PlasmicDynamicPage.jsx +2 -2
  26. package/cpa-out/nextjs-app-codegen-js/components/plasmic/create_plasmic_app/PlasmicDynamicPageServer.jsx +2 -2
  27. package/cpa-out/nextjs-app-codegen-js/components/plasmic/create_plasmic_app/plasmic.css +16 -0
  28. package/cpa-out/nextjs-app-codegen-js/components/plasmic/plasmic__default_style.css +16 -0
  29. package/cpa-out/nextjs-app-codegen-js/package.json +7 -6
  30. package/cpa-out/nextjs-app-codegen-js/plasmic.json +3 -6
  31. package/cpa-out/nextjs-app-codegen-ts/app/dynamic/[slug]/page.tsx +8 -11
  32. package/cpa-out/nextjs-app-codegen-ts/app/page.tsx +4 -4
  33. package/cpa-out/nextjs-app-codegen-ts/app/robots.ts +25 -0
  34. package/cpa-out/nextjs-app-codegen-ts/app/sitemap.ts +41 -0
  35. package/cpa-out/nextjs-app-codegen-ts/components/plasmic/create_plasmic_app/PlasmicDynamicPage.tsx +2 -2
  36. package/cpa-out/nextjs-app-codegen-ts/components/plasmic/create_plasmic_app/PlasmicDynamicPageServer.tsx +7 -13
  37. package/cpa-out/nextjs-app-codegen-ts/components/plasmic/create_plasmic_app/PlasmicHomepageServer.tsx +5 -11
  38. package/cpa-out/nextjs-app-codegen-ts/components/plasmic/create_plasmic_app/plasmic.css +16 -0
  39. package/cpa-out/nextjs-app-codegen-ts/components/plasmic/plasmic__default_style.css +16 -0
  40. package/cpa-out/nextjs-app-codegen-ts/package.json +7 -6
  41. package/cpa-out/nextjs-app-codegen-ts/plasmic.json +3 -6
  42. package/cpa-out/nextjs-app-loader-js/app/robots.js +24 -0
  43. package/cpa-out/nextjs-app-loader-js/app/sitemap.js +29 -0
  44. package/cpa-out/nextjs-app-loader-js/package.json +5 -5
  45. package/cpa-out/nextjs-app-loader-ts/app/layout.tsx +1 -5
  46. package/cpa-out/nextjs-app-loader-ts/app/robots.ts +25 -0
  47. package/cpa-out/nextjs-app-loader-ts/app/sitemap.ts +30 -0
  48. package/cpa-out/nextjs-app-loader-ts/package.json +5 -5
  49. package/cpa-out/nextjs-pages-codegen-js/components/plasmic/create_plasmic_app/PlasmicDynamicPage.jsx +2 -2
  50. package/cpa-out/nextjs-pages-codegen-js/components/plasmic/create_plasmic_app/plasmic.css +16 -0
  51. package/cpa-out/nextjs-pages-codegen-js/components/plasmic/plasmic__default_style.css +16 -0
  52. package/cpa-out/nextjs-pages-codegen-js/package.json +7 -6
  53. package/cpa-out/nextjs-pages-codegen-js/pages/robots.txt.js +25 -0
  54. package/cpa-out/nextjs-pages-codegen-js/pages/sitemap.xml.js +53 -0
  55. package/cpa-out/nextjs-pages-codegen-js/plasmic.json +3 -6
  56. package/cpa-out/nextjs-pages-codegen-js/styles/Home.module.css +8 -0
  57. package/cpa-out/nextjs-pages-codegen-ts/components/plasmic/create_plasmic_app/PlasmicDynamicPage.tsx +2 -2
  58. package/cpa-out/nextjs-pages-codegen-ts/components/plasmic/create_plasmic_app/plasmic.css +16 -0
  59. package/cpa-out/nextjs-pages-codegen-ts/components/plasmic/plasmic__default_style.css +16 -0
  60. package/cpa-out/nextjs-pages-codegen-ts/package.json +7 -6
  61. package/cpa-out/nextjs-pages-codegen-ts/pages/robots.txt.ts +27 -0
  62. package/cpa-out/nextjs-pages-codegen-ts/pages/sitemap.xml.ts +56 -0
  63. package/cpa-out/nextjs-pages-codegen-ts/plasmic.json +3 -6
  64. package/cpa-out/nextjs-pages-codegen-ts/styles/Home.module.css +8 -0
  65. package/cpa-out/nextjs-pages-loader-js/package.json +5 -5
  66. package/cpa-out/nextjs-pages-loader-js/pages/robots.txt.js +25 -0
  67. package/cpa-out/nextjs-pages-loader-js/pages/sitemap.xml.js +46 -0
  68. package/cpa-out/nextjs-pages-loader-js/styles/Home.module.css +8 -0
  69. package/cpa-out/nextjs-pages-loader-ts/package.json +5 -5
  70. package/cpa-out/nextjs-pages-loader-ts/pages/robots.txt.ts +27 -0
  71. package/cpa-out/nextjs-pages-loader-ts/pages/sitemap.xml.ts +47 -0
  72. package/cpa-out/nextjs-pages-loader-ts/styles/Home.module.css +8 -0
  73. package/cpa-out/react-codegen-js/package.json +3 -2
  74. package/cpa-out/react-codegen-js/plasmic.json +3 -6
  75. package/cpa-out/react-codegen-js/src/components/plasmic/create_plasmic_app/PlasmicDynamicPage.jsx +2 -2
  76. package/cpa-out/react-codegen-js/src/components/plasmic/create_plasmic_app/plasmic.css +16 -0
  77. package/cpa-out/react-codegen-js/src/components/plasmic/plasmic__default_style.css +16 -0
  78. package/cpa-out/react-codegen-ts/package.json +3 -2
  79. package/cpa-out/react-codegen-ts/plasmic.json +3 -6
  80. package/cpa-out/react-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicDynamicPage.tsx +2 -2
  81. package/cpa-out/react-codegen-ts/src/components/plasmic/create_plasmic_app/plasmic.css +16 -0
  82. package/cpa-out/react-codegen-ts/src/components/plasmic/plasmic__default_style.css +16 -0
  83. package/cpa-out/tanstack-codegen-ts/package.json +8 -13
  84. package/cpa-out/tanstack-codegen-ts/plasmic.json +3 -6
  85. package/cpa-out/tanstack-codegen-ts/src/components/plasmic/create_plasmic_app/PlasmicDynamicPage.tsx +3 -3
  86. package/cpa-out/tanstack-codegen-ts/src/components/plasmic/plasmic__default_style.css +16 -0
  87. package/cpa-out/tanstack-codegen-ts/src/routeTree.gen.ts +13 -13
  88. package/cpa-out/tanstack-codegen-ts/src/routes/dynamic/$slug/index.tsx +2 -2
  89. package/dist/gatsby/gatsby.js +23 -17
  90. package/dist/index.js +6 -0
  91. package/dist/lib.d.ts +2 -1
  92. package/dist/lib.js +9 -4
  93. package/dist/nextjs/nextjs.js +30 -6
  94. package/dist/nextjs/templates/app-seo.d.ts +4 -0
  95. package/dist/nextjs/templates/app-seo.js +92 -0
  96. package/dist/nextjs/templates/pages-seo.d.ts +4 -0
  97. package/dist/nextjs/templates/pages-seo.js +121 -0
  98. package/dist/react/react.js +9 -4
  99. package/dist/tanstack/tanstack.js +12 -5
  100. package/dist/utils/codegen.d.ts +3 -0
  101. package/dist/utils/codegen.js +17 -4
  102. package/dist/utils/file-utils.d.ts +2 -2
  103. package/dist/utils/file-utils.js +2 -1
  104. package/dist/utils/npm-utils.d.ts +24 -9
  105. package/dist/utils/npm-utils.js +66 -21
  106. package/dist/utils/strategy.d.ts +5 -1
  107. package/dist/utils/types.d.ts +1 -0
  108. package/package.json +2 -4
  109. package/run-cpa.ts +17 -6
  110. package/src/gatsby/gatsby.ts +40 -22
  111. package/src/index.ts +9 -0
  112. package/src/lib.ts +18 -6
  113. package/src/nextjs/nextjs.ts +85 -7
  114. package/src/nextjs/templates/app-seo.ts +104 -0
  115. package/src/nextjs/templates/pages-seo.ts +131 -0
  116. package/src/react/react.ts +11 -4
  117. package/src/tanstack/tanstack.ts +19 -6
  118. package/src/utils/codegen.ts +28 -6
  119. package/src/utils/file-utils.ts +6 -2
  120. package/src/utils/npm-utils.ts +76 -23
  121. package/src/utils/strategy.ts +10 -1
  122. package/src/utils/types.ts +1 -0
@@ -0,0 +1,104 @@
1
+ import { ifTs } from "../../utils/file-utils";
2
+ import { JsOrTs } from "../../utils/types";
3
+
4
+ const getSiteUrl = `// Set NEXT_PUBLIC_SITE_URL to pin the origin and keep this route static;
5
+ // otherwise it is derived per-request, which opts the route out of prerendering.
6
+ async function getSiteUrl() {
7
+ if (process.env.NEXT_PUBLIC_SITE_URL) {
8
+ return process.env.NEXT_PUBLIC_SITE_URL;
9
+ }
10
+ if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {
11
+ return \`https://\${process.env.VERCEL_PROJECT_PRODUCTION_URL}\`;
12
+ }
13
+ const h = await headers();
14
+ const proto = h.get("x-forwarded-proto") ?? "http";
15
+ return \`\${proto}://\${h.get("x-forwarded-host") ?? h.get("host")}\`;
16
+ }`;
17
+
18
+ function makeHeader(jsOrTs: JsOrTs) {
19
+ return `${ifTs(
20
+ jsOrTs,
21
+ `import type { MetadataRoute } from "next";\n`
22
+ )}import { headers } from "next/headers";\n`;
23
+ }
24
+
25
+ function makeSignature(jsOrTs: JsOrTs) {
26
+ return `export default async function sitemap()${ifTs(
27
+ jsOrTs,
28
+ ": Promise<MetadataRoute.Sitemap>"
29
+ )} {`;
30
+ }
31
+
32
+ export function makeSitemap_app_loader(jsOrTs: JsOrTs): string {
33
+ return `import { PLASMIC } from "@/plasmic-init";
34
+ ${makeHeader(jsOrTs)}
35
+ export const revalidate = 60;
36
+
37
+ ${getSiteUrl}
38
+
39
+ ${makeSignature(jsOrTs)}
40
+ const siteUrl = await getSiteUrl();
41
+
42
+ // fetchPages() drops pages whose path takes a param, e.g. "/blog/[slug]".
43
+ const pages = await PLASMIC.fetchPages();
44
+
45
+ return pages.map((page) => ({
46
+ url: new URL(page.path, siteUrl).toString(),
47
+ }));
48
+ }
49
+ `;
50
+ }
51
+
52
+ export function makeSitemap_app_codegen(jsOrTs: JsOrTs): string {
53
+ const projects =
54
+ jsOrTs === "ts"
55
+ ? `(plasmicJson.projects as {
56
+ components: { componentType: string; path?: string }[];
57
+ }[])`
58
+ : "plasmicJson.projects";
59
+
60
+ return `${makeHeader(jsOrTs)}import plasmicJson from "../plasmic.json";
61
+
62
+ ${getSiteUrl}
63
+
64
+ ${makeSignature(jsOrTs)}
65
+ const siteUrl = await getSiteUrl();
66
+
67
+ return ${projects}
68
+ .flatMap((project) => project.components)
69
+ .flatMap((component) =>
70
+ component.componentType === "page" &&
71
+ component.path &&
72
+ !component.path.includes("[")
73
+ ? [
74
+ {
75
+ // next.config sets trailingSlash.
76
+ url: new URL(
77
+ component.path.replace(/\\/?$/, "/"),
78
+ siteUrl
79
+ ).toString(),
80
+ },
81
+ ]
82
+ : []
83
+ );
84
+ }
85
+ `;
86
+ }
87
+
88
+ export function makeRobots_app(jsOrTs: JsOrTs): string {
89
+ return `${makeHeader(jsOrTs)}
90
+ ${getSiteUrl}
91
+
92
+ export default async function robots()${ifTs(
93
+ jsOrTs,
94
+ ": Promise<MetadataRoute.Robots>"
95
+ )} {
96
+ const siteUrl = await getSiteUrl();
97
+
98
+ return {
99
+ rules: { userAgent: "*", allow: "/" },
100
+ sitemap: new URL("/sitemap.xml", siteUrl).toString(),
101
+ };
102
+ }
103
+ `;
104
+ }
@@ -0,0 +1,131 @@
1
+ import { ifTs } from "../../utils/file-utils";
2
+ import { JsOrTs } from "../../utils/types";
3
+
4
+ const siteUrl = ` // Set NEXT_PUBLIC_SITE_URL to pin the origin;
5
+ // otherwise it is derived per-request from the request headers.
6
+ const siteUrl =
7
+ process.env.NEXT_PUBLIC_SITE_URL ??
8
+ (process.env.VERCEL_PROJECT_PRODUCTION_URL
9
+ ? \`https://\${process.env.VERCEL_PROJECT_PRODUCTION_URL}\`
10
+ : \`\${req.headers["x-forwarded-proto"] ?? "http"}://\${
11
+ req.headers["x-forwarded-host"] ?? req.headers.host
12
+ }\`);`;
13
+
14
+ function makeHeader(jsOrTs: JsOrTs) {
15
+ return ifTs(jsOrTs, `import type { GetServerSideProps } from "next";\n`);
16
+ }
17
+
18
+ function makeSignature(jsOrTs: JsOrTs) {
19
+ return `export const getServerSideProps${ifTs(
20
+ jsOrTs,
21
+ ": GetServerSideProps"
22
+ )} = async ({ req, res }) => {`;
23
+ }
24
+
25
+ function makeSitemapXml(jsOrTs: JsOrTs) {
26
+ return `function escapeXml(str${ifTs(jsOrTs, ": string")}) {
27
+ return str
28
+ .replace(/&/g, "&amp;")
29
+ .replace(/</g, "&lt;")
30
+ .replace(/>/g, "&gt;")
31
+ .replace(/"/g, "&quot;")
32
+ .replace(/'/g, "&apos;");
33
+ }
34
+
35
+ function makeSitemapXml(urls${ifTs(jsOrTs, ": string[]")}) {
36
+ const entries = urls.map(
37
+ (url) => \` <url><loc>\${escapeXml(url)}</loc></url>\`
38
+ );
39
+ return \`<?xml version="1.0" encoding="UTF-8"?>
40
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
41
+ \${entries.join("\\n")}
42
+ </urlset>
43
+ \`;
44
+ }`;
45
+ }
46
+
47
+ export function makeSitemap_pages_loader(jsOrTs: JsOrTs): string {
48
+ return `import { PLASMIC } from "@/plasmic-init";
49
+ ${makeHeader(jsOrTs)}
50
+ ${makeSitemapXml(jsOrTs)}
51
+
52
+ ${makeSignature(jsOrTs)}
53
+ ${siteUrl}
54
+
55
+ // fetchPages() drops pages whose path takes a param, e.g. "/blog/[slug]".
56
+ const pages = await PLASMIC.fetchPages();
57
+ const urls = pages.map((page) => new URL(page.path, siteUrl).toString());
58
+
59
+ res.setHeader("Content-Type", "application/xml; charset=utf-8");
60
+ res.write(makeSitemapXml(urls));
61
+ res.end();
62
+ return { props: {} };
63
+ };
64
+
65
+ export default function Sitemap() {
66
+ return null;
67
+ }
68
+ `;
69
+ }
70
+
71
+ export function makeSitemap_pages_codegen(jsOrTs: JsOrTs): string {
72
+ const projects =
73
+ jsOrTs === "ts"
74
+ ? `(plasmicJson.projects as {
75
+ components: { componentType: string; path?: string }[];
76
+ }[])`
77
+ : "plasmicJson.projects";
78
+
79
+ return `${makeHeader(jsOrTs)}import plasmicJson from "../plasmic.json";
80
+
81
+ ${makeSitemapXml(jsOrTs)}
82
+
83
+ ${makeSignature(jsOrTs)}
84
+ ${siteUrl}
85
+
86
+ const urls = ${projects}
87
+ .flatMap((project) => project.components)
88
+ .flatMap((component) =>
89
+ component.componentType === "page" &&
90
+ component.path &&
91
+ !component.path.includes("[")
92
+ ? // next.config sets trailingSlash.
93
+ [new URL(component.path.replace(/\\/?$/, "/"), siteUrl).toString()]
94
+ : []
95
+ );
96
+
97
+ res.setHeader("Content-Type", "application/xml; charset=utf-8");
98
+ res.write(makeSitemapXml(urls));
99
+ res.end();
100
+ return { props: {} };
101
+ };
102
+
103
+ export default function Sitemap() {
104
+ return null;
105
+ }
106
+ `;
107
+ }
108
+
109
+ export function makeRobots_pages(jsOrTs: JsOrTs): string {
110
+ return `${ifTs(
111
+ jsOrTs,
112
+ `import type { GetServerSideProps } from "next";\n\n`
113
+ )}${makeSignature(jsOrTs)}
114
+ ${siteUrl}
115
+
116
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
117
+ res.write(
118
+ \`User-agent: *\\nAllow: /\\n\\nSitemap: \${new URL(
119
+ "/sitemap.xml",
120
+ siteUrl
121
+ ).toString()}\\n\`
122
+ );
123
+ res.end();
124
+ return { props: {} };
125
+ };
126
+
127
+ export default function Robots() {
128
+ return null;
129
+ }
130
+ `;
131
+ }
@@ -9,12 +9,12 @@ import {
9
9
  generateWelcomePage,
10
10
  getPlasmicConfig,
11
11
  } from "../utils/file-utils";
12
- import { installUpgrade } from "../utils/npm-utils";
12
+ import { initYarnBerryProject, installUpgrade } from "../utils/npm-utils";
13
13
  import { CPAStrategy } from "../utils/strategy";
14
14
 
15
15
  export const reactStrategy: CPAStrategy = {
16
16
  create: async (args) => {
17
- const { projectPath, jsOrTs } = args;
17
+ const { projectPath, jsOrTs, packageManager } = args;
18
18
  let { template } = args;
19
19
 
20
20
  /* create-vite package checks if the targetDir doesn't exist then it creates the targetDir in the
@@ -45,14 +45,19 @@ export const reactStrategy: CPAStrategy = {
45
45
 
46
46
  const templateArg = template ? ` --template ${template}` : "";
47
47
  await spawnOrFail(`${createCommand}${templateArg}`);
48
+
49
+ if (packageManager === "yarn2") {
50
+ await initYarnBerryProject(fullProjectPath);
51
+ }
48
52
  },
49
- installDeps: async ({ projectPath, scheme }) => {
53
+ installDeps: async ({ projectPath, scheme, packageManager }) => {
50
54
  if (scheme === "loader") {
51
55
  return await installUpgrade("@plasmicapp/loader-react", {
52
56
  workingDir: projectPath,
57
+ packageManager,
53
58
  });
54
59
  } else {
55
- return await installCodegenDeps({ projectPath });
60
+ return await installCodegenDeps({ projectPath, packageManager });
56
61
  }
57
62
  },
58
63
  overwriteConfig: async (args) => {
@@ -81,6 +86,7 @@ export const reactStrategy: CPAStrategy = {
81
86
  projectId,
82
87
  projectPath,
83
88
  jsOrTs,
89
+ packageManager,
84
90
  }) => {
85
91
  if (scheme === "loader") {
86
92
  // Nothing to do
@@ -92,6 +98,7 @@ export const reactStrategy: CPAStrategy = {
92
98
  projectId,
93
99
  projectApiToken,
94
100
  projectPath,
101
+ packageManager,
95
102
  });
96
103
 
97
104
  // Pick a page for the entry point App.tsx page
@@ -3,14 +3,18 @@ import path from "path";
3
3
  import { spawnOrFail } from "../utils/cmd-utils";
4
4
  import { installCodegenDeps, runCodegenSync } from "../utils/codegen";
5
5
  import { deleteGlob } from "../utils/file-utils";
6
- import { installUpgrade } from "../utils/npm-utils";
6
+ import {
7
+ initYarnBerryProject,
8
+ installUpgrade,
9
+ packageManagerCommand,
10
+ } from "../utils/npm-utils";
7
11
  import { CPAStrategy, GenerateFilesArgs } from "../utils/strategy";
8
12
  import { makePlasmicHostPage_fileRouter_codegen } from "./templates/file-router/plasmic-host";
9
13
  import { makeCustomRoot_file_router_codegen } from "./templates/file-router/root";
10
14
 
11
15
  export const tanstackStrategy: CPAStrategy = {
12
16
  create: async (args) => {
13
- const { projectPath, template } = args;
17
+ const { projectPath, template, packageManager } = args;
14
18
  if (template) {
15
19
  console.warn(
16
20
  `Warning: Ignoring template '${template}' (argument is not supported by TanStack).`
@@ -39,27 +43,34 @@ export const tanstackStrategy: CPAStrategy = {
39
43
  "--framework React",
40
44
  "--router-only",
41
45
  "--no-toolchain",
42
- "--package-manager yarn",
46
+ `--package-manager ${packageManagerCommand(packageManager)}`,
43
47
  "--git",
44
48
  "--no-intent",
45
49
  "--no-examples",
46
50
  "--yes",
51
+ // Berry must install into the project we set up below, not the default PnP one
52
+ ...(packageManager === "yarn2" ? ["--no-install"] : []),
47
53
  ].join(" ");
48
54
 
49
55
  await spawnOrFail(createCommand);
50
56
 
57
+ if (packageManager === "yarn2") {
58
+ await initYarnBerryProject(fullProjectPath);
59
+ }
60
+
51
61
  // Install peer-dep of @tanstack/react-router-with-query
52
62
  await installUpgrade("@tanstack/react-query", {
53
63
  workingDir: projectPath,
64
+ packageManager,
54
65
  });
55
66
  },
56
- installDeps: async ({ scheme, projectPath }) => {
67
+ installDeps: async ({ scheme, projectPath, packageManager }) => {
57
68
  if (scheme === "loader") {
58
69
  throw new Error(
59
70
  "Plasmic loader scheme is not supported for TanStack platform. Please use the codegen scheme instead."
60
71
  );
61
72
  } else {
62
- return await installCodegenDeps({ projectPath });
73
+ return await installCodegenDeps({ projectPath, packageManager });
63
74
  }
64
75
  },
65
76
  overwriteConfig: async (args) => {
@@ -106,7 +117,8 @@ export const tanstackStrategy: CPAStrategy = {
106
117
  };
107
118
 
108
119
  async function generateFilesFileRouterTemplate(args: GenerateFilesArgs) {
109
- const { projectPath, scheme, projectId, projectApiToken } = args;
120
+ const { projectPath, scheme, projectId, projectApiToken, packageManager } =
121
+ args;
110
122
 
111
123
  // Delete existing pages
112
124
  deleteGlob(path.join(projectPath, "src/routes", "*.*"));
@@ -136,6 +148,7 @@ async function generateFilesFileRouterTemplate(args: GenerateFilesArgs) {
136
148
  projectId,
137
149
  projectApiToken,
138
150
  projectPath,
151
+ packageManager,
139
152
  });
140
153
  }
141
154
  }
@@ -1,18 +1,29 @@
1
+ import { promises as fs } from "fs";
2
+ import path from "path";
1
3
  import { banner } from "../lib";
2
4
  import { spawnOrFail } from "../utils/cmd-utils";
3
- import { installUpgrade } from "../utils/npm-utils";
5
+ import { installUpgrade, packageManagerExec } from "../utils/npm-utils";
6
+ import { PackageManagerType } from "../utils/types";
4
7
 
5
- export async function installCodegenDeps(opts: { projectPath: string }) {
6
- const { projectPath } = opts;
7
- return await installUpgrade("@plasmicapp/cli", { workingDir: projectPath });
8
+ export async function installCodegenDeps(opts: {
9
+ projectPath: string;
10
+ packageManager: PackageManagerType;
11
+ }) {
12
+ const { projectPath, packageManager } = opts;
13
+ // Generated pages import @plasmicapp/host directly
14
+ return await installUpgrade("@plasmicapp/cli @plasmicapp/host", {
15
+ workingDir: projectPath,
16
+ packageManager,
17
+ });
8
18
  }
9
19
 
10
20
  export async function runCodegenSync(opts: {
11
21
  projectId: string;
12
22
  projectApiToken: string | undefined;
13
23
  projectPath: string;
24
+ packageManager: PackageManagerType;
14
25
  }) {
15
- const { projectId, projectApiToken, projectPath } = opts;
26
+ const { projectId, projectApiToken, projectPath, packageManager } = opts;
16
27
 
17
28
  banner("SYNCING PLASMIC COMPONENTS");
18
29
 
@@ -20,5 +31,16 @@ export async function runCodegenSync(opts: {
20
31
  ? `${projectId}:${projectApiToken}`
21
32
  : projectId;
22
33
 
23
- await spawnOrFail(`npx plasmic sync --yes -p ${project}`, projectPath);
34
+ await spawnOrFail(
35
+ `${packageManagerExec(packageManager)} plasmic sync --yes -p ${project}`,
36
+ projectPath
37
+ );
38
+
39
+ // Pin the package manager, so syncs don't have to guess from lockfiles
40
+ const configPath = path.join(projectPath, "plasmic.json");
41
+ const config = JSON.parse(await fs.readFile(configPath, "utf8"));
42
+ await fs.writeFile(
43
+ configPath,
44
+ JSON.stringify({ ...config, packageManager }, undefined, 2) + "\n"
45
+ );
24
46
  }
@@ -10,7 +10,7 @@ import { README } from "../templates/readme";
10
10
  import { WELCOME_PAGE } from "../templates/welcomePage";
11
11
  import { ensure } from "./lang-utils";
12
12
  import { installUpgrade } from "./npm-utils";
13
- import { JsOrTs, PlatformType } from "./types";
13
+ import { JsOrTs, PackageManagerType, PlatformType } from "./types";
14
14
 
15
15
  /**
16
16
  * Runs the search pattern through `glob` and deletes all resulting files
@@ -215,12 +215,16 @@ export async function getPlasmicConfig(
215
215
 
216
216
  // Create tsconfig.json if it doesn't exist
217
217
  // this will force Plasmic to recognize Typescript
218
- export async function ensureTsconfig(projectPath: string): Promise<void> {
218
+ export async function ensureTsconfig(
219
+ projectPath: string,
220
+ packageManager: PackageManagerType
221
+ ): Promise<void> {
219
222
  const tsconfigPath = path.join(projectPath, "tsconfig.json");
220
223
  if (!existsSync(tsconfigPath)) {
221
224
  await fs.writeFile(tsconfigPath, "");
222
225
  const installTsResult = await installUpgrade("typescript @types/react", {
223
226
  workingDir: projectPath,
227
+ packageManager,
224
228
  });
225
229
  if (!installTsResult) {
226
230
  throw new Error("Failed to install Typescript");
@@ -2,10 +2,11 @@
2
2
  @typescript-eslint/no-var-requires: 0,
3
3
  */
4
4
  import * as execa from "execa";
5
- import { existsSync } from "fs";
5
+ import { promises as fs } from "fs";
6
6
  import path from "path";
7
7
  import * as semver from "semver";
8
8
  import updateNotifier from "update-notifier";
9
+ import { PackageManagerType } from "./types";
9
10
 
10
11
  /**
11
12
  * Call this to check if there's an update available
@@ -54,6 +55,13 @@ export async function spawn(
54
55
  return cp.exitCode === 0;
55
56
  }
56
57
 
58
+ interface InstallOpts {
59
+ global?: boolean;
60
+ dev?: boolean;
61
+ workingDir?: string;
62
+ packageManager: PackageManagerType;
63
+ }
64
+
57
65
  /**
58
66
  * Install a package using either `npm` or `yarn`
59
67
  * @param pkg - package name
@@ -62,7 +70,7 @@ export async function spawn(
62
70
  */
63
71
  export async function installUpgrade(
64
72
  pkg: string,
65
- opts: { global?: boolean; dev?: boolean; workingDir?: string } = {}
73
+ opts: InstallOpts
66
74
  ): Promise<boolean> {
67
75
  const cmd = installCommand(pkg, opts);
68
76
  const r = await spawn(cmd, opts.workingDir);
@@ -83,11 +91,8 @@ export async function installUpgrade(
83
91
  * @param opts
84
92
  * @returns
85
93
  */
86
- function installCommand(
87
- pkg: string,
88
- opts: { global?: boolean; dev?: boolean; workingDir?: string } = {}
89
- ): string {
90
- const mgr = detectPackageManager(opts.workingDir);
94
+ function installCommand(pkg: string, opts: InstallOpts): string {
95
+ const mgr = opts.packageManager;
91
96
  if (mgr === "yarn") {
92
97
  if (opts.global) {
93
98
  return `yarn global add ${pkg}`;
@@ -96,6 +101,14 @@ function installCommand(
96
101
  } else {
97
102
  return `yarn add --ignore-scripts -W ${pkg}`;
98
103
  }
104
+ } else if (mgr === "yarn2") {
105
+ if (opts.global) {
106
+ return `npm install -g ${pkg}`;
107
+ } else if (opts.dev) {
108
+ return `yarn add -D ${pkg}`;
109
+ } else {
110
+ return `yarn add ${pkg}`;
111
+ }
99
112
  } else if (mgr === "pnpm") {
100
113
  if (opts.global) {
101
114
  return `pnpm add -g ${pkg}`;
@@ -116,23 +129,63 @@ function installCommand(
116
129
  }
117
130
 
118
131
  /**
119
- * Detect if you should use `npm` or `yarn`
120
- * @param dir
121
- * @returns
132
+ * The package manager to install with: the one requested, else the one we were run
133
+ * with. `npx` overwrites npm_config_user_agent, so this must be read before spawning
134
+ * nested create commands.
122
135
  */
123
- export function detectPackageManager(dir?: string): "yarn" | "pnpm" | "npm" {
124
- // We should look only inside the directory instead of looking to the ancestors
125
- // with findupSync, the reason for this is that the current gatsby template
126
- // uses npm, so if the user has some yarn.lock in a parent directory it's
127
- // going to run yarn commands, this is going to trigger an error with sharp
136
+ export function resolvePackageManager(
137
+ requested?: PackageManagerType
138
+ ): PackageManagerType {
139
+ if (requested) {
140
+ return requested === "yarn"
141
+ ? yarnVariant(installedYarnVersion())
142
+ : requested;
143
+ }
144
+ const [name, version] = (process.env.npm_config_user_agent ?? "")
145
+ .split(" ")[0]
146
+ .split("/");
147
+ if (name === "yarn") {
148
+ return yarnVariant(version);
149
+ }
150
+ return name === "pnpm" ? "pnpm" : "npm";
151
+ }
128
152
 
129
- const yarnLock = existsSync(path.join(dir ? dir : "", "yarn.lock"));
130
- const pnpmLock = existsSync(path.join(dir ? dir : "", "pnpm-lock.yaml"));
131
- if (yarnLock) {
132
- return "yarn";
133
- } else if (pnpmLock) {
134
- return "pnpm";
135
- } else {
136
- return "npm";
153
+ function yarnVariant(version: string | undefined): PackageManagerType {
154
+ const parsed = version ? semver.coerce(version) : null;
155
+ return parsed && parsed.major >= 2 ? "yarn2" : "yarn";
156
+ }
157
+
158
+ function installedYarnVersion(): string | undefined {
159
+ try {
160
+ return execa.commandSync("yarn --version").stdout.trim();
161
+ } catch {
162
+ return undefined;
137
163
  }
138
164
  }
165
+
166
+ /** Scaffolders and run scripts only know the `yarn` command. */
167
+ export function packageManagerCommand(
168
+ mgr: PackageManagerType
169
+ ): "npm" | "yarn" | "pnpm" {
170
+ return mgr === "yarn2" ? "yarn" : mgr;
171
+ }
172
+
173
+ /** Run a locally installed bin. `npx` can't see Yarn Berry's PnP installs. */
174
+ export function packageManagerExec(mgr: PackageManagerType): string {
175
+ const cmd = packageManagerCommand(mgr);
176
+ return cmd === "yarn" ? "yarn" : cmd === "pnpm" ? "pnpm exec" : "npx";
177
+ }
178
+
179
+ /**
180
+ * Make a freshly scaffolded app its own Yarn Berry project. Without a lockfile Berry
181
+ * claims the app for the nearest ancestor project, and its default PnP linker can't
182
+ * load CommonJS from Node 24's ESM resolver (EBADF), which breaks every framework we
183
+ * scaffold. Must run before anything invokes yarn in `projectPath`.
184
+ */
185
+ export async function initYarnBerryProject(projectPath: string): Promise<void> {
186
+ await fs.writeFile(path.join(projectPath, "yarn.lock"), "");
187
+ await fs.writeFile(
188
+ path.join(projectPath, ".yarnrc.yml"),
189
+ "nodeLinker: node-modules\n"
190
+ );
191
+ }
@@ -1,10 +1,17 @@
1
- import { JsOrTs, PlatformOptions, SchemeType } from "./types";
1
+ import {
2
+ JsOrTs,
3
+ PackageManagerType,
4
+ PlatformOptions,
5
+ SchemeType,
6
+ } from "./types";
2
7
 
3
8
  export interface CreateArgs {
4
9
  projectPath: string;
10
+ projectId: string;
5
11
  template?: string;
6
12
  jsOrTs: JsOrTs;
7
13
  platformOptions: PlatformOptions;
14
+ packageManager: PackageManagerType;
8
15
  }
9
16
 
10
17
  export interface ConfigArgs {
@@ -23,6 +30,7 @@ export interface GenerateFilesArgs {
23
30
  projectId: string;
24
31
  projectApiToken: string | undefined;
25
32
  platformOptions: PlatformOptions;
33
+ packageManager: PackageManagerType;
26
34
  }
27
35
 
28
36
  export interface BuildArgs {
@@ -34,6 +42,7 @@ export interface InstallArgs {
34
42
  scheme: SchemeType;
35
43
  projectPath: string;
36
44
  jsOrTs: JsOrTs;
45
+ packageManager: PackageManagerType;
37
46
  }
38
47
 
39
48
  // TODO: Remove this interface and use a single "create" entry point.
@@ -6,6 +6,7 @@ export type PlatformOptions = {
6
6
  };
7
7
  };
8
8
  export type SchemeType = "codegen" | "loader";
9
+ export type PackageManagerType = "npm" | "yarn" | "yarn2" | "pnpm";
9
10
 
10
11
  export type PlasmicCssImport = {
11
12
  projectId: string;