@shopify/cli-hydrogen 5.2.0 → 5.2.2

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 (33) hide show
  1. package/dist/commands/hydrogen/dev.js +1 -2
  2. package/dist/commands/hydrogen/generate/route.test.js +0 -1
  3. package/dist/commands/hydrogen/init.js +3 -3
  4. package/dist/commands/hydrogen/preview.js +1 -2
  5. package/dist/generator-templates/starter/app/entry.server.tsx +9 -1
  6. package/dist/generator-templates/starter/app/root.tsx +9 -6
  7. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +1 -1
  8. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +1 -1
  9. package/dist/generator-templates/starter/package.json +5 -5
  10. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +2 -2
  11. package/dist/generator-templates/starter/tsconfig.json +1 -0
  12. package/dist/lib/file.js +1 -1
  13. package/dist/lib/flags.js +3 -3
  14. package/dist/lib/format-code.js +4 -4
  15. package/dist/lib/graphql/admin/client.js +18 -0
  16. package/dist/lib/graphql/admin/client.test.js +28 -3
  17. package/dist/lib/mini-oxygen.js +1 -0
  18. package/dist/lib/setups/css/index.js +4 -2
  19. package/dist/lib/setups/routes/generate.js +11 -28
  20. package/dist/lib/setups/routes/generate.test.js +1 -3
  21. package/dist/lib/transpile-ts.js +4 -3
  22. package/dist/virtual-routes/components/HydrogenLogoBaseBW.jsx +22 -19
  23. package/dist/virtual-routes/components/HydrogenLogoBaseColor.jsx +37 -33
  24. package/dist/virtual-routes/components/IconBanner.jsx +282 -279
  25. package/dist/virtual-routes/components/IconDiscord.jsx +11 -10
  26. package/dist/virtual-routes/components/IconError.jsx +51 -48
  27. package/dist/virtual-routes/components/IconGithub.jsx +13 -12
  28. package/dist/virtual-routes/components/IconTwitter.jsx +11 -10
  29. package/dist/virtual-routes/components/Layout.jsx +2 -1
  30. package/dist/virtual-routes/routes/index.jsx +194 -105
  31. package/dist/virtual-routes/virtual-root.jsx +62 -8
  32. package/oclif.manifest.json +3 -3
  33. package/package.json +4 -4
@@ -16,7 +16,6 @@ import { addVirtualRoutes } from '../../lib/virtual-routes.js';
16
16
  import { spawnCodegenProcess } from '../../lib/codegen.js';
17
17
  import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
18
18
  import { getConfig } from '../../lib/shopify-config.js';
19
- import { findPort } from '../../lib/find-port.js';
20
19
  import { setupLiveReload } from '../../lib/live-reload.js';
21
20
  import { checkRemixVersions } from '../../lib/remix-version-check.js';
22
21
 
@@ -110,7 +109,7 @@ async function runDev({
110
109
  return;
111
110
  miniOxygen = await startMiniOxygen({
112
111
  root,
113
- port: await findPort(portFlag),
112
+ port: portFlag,
114
113
  watch: !liveReload,
115
114
  buildPathWorkerFile,
116
115
  buildPathClient,
@@ -16,7 +16,6 @@ describe("runGenerate", () => {
16
16
  it("calls route generation and renders the result", async () => {
17
17
  vi.mocked(generateRoutes).mockResolvedValue({
18
18
  isTypescript: true,
19
- transpilerOptions: {},
20
19
  formatOptions: {},
21
20
  v2Flags: {},
22
21
  routeGroups: {},
@@ -6,7 +6,7 @@ import { AbortError } from '@shopify/cli-kit/node/error';
6
6
  import { AbortController } from '@shopify/cli-kit/node/abort';
7
7
  import { commonFlags, flagsToCamelObject, parseProcessFlags } from '../../lib/flags.js';
8
8
  import { checkHydrogenVersion } from '../../lib/check-version.js';
9
- import { SETUP_CSS_STRATEGIES } from './../../lib/setups/css/index.js';
9
+ import { STYLING_CHOICES } from './../../lib/setups/css/index.js';
10
10
  import { I18N_CHOICES } from '../../lib/setups/i18n/index.js';
11
11
  import { supressNodeExperimentalWarnings } from '../../lib/process.js';
12
12
  import { setupRemoteTemplate, setupLocalStarterTemplate } from '../../lib/onboarding/index.js';
@@ -58,9 +58,9 @@ class Init extends Command {
58
58
  `Invalid URL structure strategy: ${flags.markets}. Must be one of ${I18N_CHOICES.join(", ")}`
59
59
  );
60
60
  }
61
- if (flags.styling && !SETUP_CSS_STRATEGIES.includes(flags.styling)) {
61
+ if (flags.styling && !STYLING_CHOICES.includes(flags.styling)) {
62
62
  throw new AbortError(
63
- `Invalid styling strategy: ${flags.styling}. Must be one of ${SETUP_CSS_STRATEGIES.join(", ")}`
63
+ `Invalid styling strategy: ${flags.styling}. Must be one of ${STYLING_CHOICES.join(", ")}`
64
64
  );
65
65
  }
66
66
  await runInit(flagsToCamelObject(flags));
@@ -3,7 +3,6 @@ import { muteDevLogs } from '../../lib/log.js';
3
3
  import { getProjectPaths } from '../../lib/remix-config.js';
4
4
  import { commonFlags, DEFAULT_PORT } from '../../lib/flags.js';
5
5
  import { startMiniOxygen } from '../../lib/mini-oxygen.js';
6
- import { findPort } from '../../lib/find-port.js';
7
6
 
8
7
  class Preview extends Command {
9
8
  static description = "Runs a Hydrogen storefront in an Oxygen worker for production.";
@@ -26,7 +25,7 @@ async function runPreview({
26
25
  const { root, buildPathWorkerFile, buildPathClient } = getProjectPaths(appPath);
27
26
  const miniOxygen = await startMiniOxygen({
28
27
  root,
29
- port: await findPort(port),
28
+ port,
30
29
  buildPathClient,
31
30
  buildPathWorkerFile
32
31
  });
@@ -2,6 +2,7 @@ import type {EntryContext} from '@shopify/remix-oxygen';
2
2
  import {RemixServer} from '@remix-run/react';
3
3
  import isbot from 'isbot';
4
4
  import {renderToReadableStream} from 'react-dom/server';
5
+ import {createContentSecurityPolicy} from '@shopify/hydrogen';
5
6
 
6
7
  export default async function handleRequest(
7
8
  request: Request,
@@ -9,9 +10,14 @@ export default async function handleRequest(
9
10
  responseHeaders: Headers,
10
11
  remixContext: EntryContext,
11
12
  ) {
13
+ const {nonce, header, NonceProvider} = createContentSecurityPolicy();
14
+
12
15
  const body = await renderToReadableStream(
13
- <RemixServer context={remixContext} url={request.url} />,
16
+ <NonceProvider>
17
+ <RemixServer context={remixContext} url={request.url} />
18
+ </NonceProvider>,
14
19
  {
20
+ nonce,
15
21
  signal: request.signal,
16
22
  onError(error) {
17
23
  // eslint-disable-next-line no-console
@@ -26,6 +32,8 @@ export default async function handleRequest(
26
32
  }
27
33
 
28
34
  responseHeaders.set('Content-Type', 'text/html');
35
+ responseHeaders.set('Content-Security-Policy', header);
36
+
29
37
  return new Response(body, {
30
38
  headers: responseHeaders,
31
39
  status: responseStatusCode,
@@ -1,3 +1,4 @@
1
+ import {useNonce} from '@shopify/hydrogen';
1
2
  import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
2
3
  import {
3
4
  Links,
@@ -98,6 +99,7 @@ export async function loader({context}: LoaderArgs) {
98
99
  }
99
100
 
100
101
  export default function App() {
102
+ const nonce = useNonce();
101
103
  const data = useLoaderData<typeof loader>();
102
104
 
103
105
  return (
@@ -112,9 +114,9 @@ export default function App() {
112
114
  <Layout {...data}>
113
115
  <Outlet />
114
116
  </Layout>
115
- <ScrollRestoration />
116
- <Scripts />
117
- <LiveReload />
117
+ <ScrollRestoration nonce={nonce} />
118
+ <Scripts nonce={nonce} />
119
+ <LiveReload nonce={nonce} />
118
120
  </body>
119
121
  </html>
120
122
  );
@@ -123,6 +125,7 @@ export default function App() {
123
125
  export function ErrorBoundary() {
124
126
  const error = useRouteError();
125
127
  const [root] = useMatches();
128
+ const nonce = useNonce();
126
129
  let errorMessage = 'Unknown error';
127
130
  let errorStatus = 500;
128
131
 
@@ -153,9 +156,9 @@ export function ErrorBoundary() {
153
156
  )}
154
157
  </div>
155
158
  </Layout>
156
- <ScrollRestoration />
157
- <Scripts />
158
- <LiveReload />
159
+ <ScrollRestoration nonce={nonce} />
160
+ <Scripts nonce={nonce} />
161
+ <LiveReload nonce={nonce} />
159
162
  </body>
160
163
  </html>
161
164
  );
@@ -3,7 +3,7 @@ import {Link, useLoaderData, type V2_MetaFunction} from '@remix-run/react';
3
3
  import {Pagination, getPaginationVariables} from '@shopify/hydrogen';
4
4
 
5
5
  export const meta: V2_MetaFunction = () => {
6
- return [{title: `Hydrogen | Logs`}];
6
+ return [{title: `Hydrogen | Blogs`}];
7
7
  };
8
8
 
9
9
  export const loader = async ({request, context: {storefront}}: LoaderArgs) => {
@@ -174,8 +174,8 @@ const COLLECTION_QUERY = `#graphql
174
174
  pageInfo {
175
175
  hasPreviousPage
176
176
  hasNextPage
177
- hasNextPage
178
177
  endCursor
178
+ startCursor
179
179
  }
180
180
  }
181
181
  }
@@ -15,8 +15,8 @@
15
15
  "dependencies": {
16
16
  "@remix-run/react": "1.19.1",
17
17
  "@shopify/cli": "3.48.0",
18
- "@shopify/cli-hydrogen": "^5.2.0",
19
- "@shopify/hydrogen": "^2023.7.3",
18
+ "@shopify/cli-hydrogen": "^5.2.2",
19
+ "@shopify/hydrogen": "^2023.7.6",
20
20
  "@shopify/remix-oxygen": "^1.1.3",
21
21
  "graphql": "^16.6.0",
22
22
  "graphql-tag": "^2.12.6",
@@ -30,12 +30,12 @@
30
30
  "@shopify/prettier-config": "^1.1.2",
31
31
  "@total-typescript/ts-reset": "^0.4.2",
32
32
  "@types/eslint": "^8.4.10",
33
- "@types/react": "^18.0.20",
34
- "@types/react-dom": "^18.0.6",
33
+ "@types/react": "^18.2.20",
34
+ "@types/react-dom": "^18.2.7",
35
35
  "eslint": "^8.20.0",
36
36
  "eslint-plugin-hydrogen": "0.12.2",
37
37
  "prettier": "^2.8.4",
38
- "typescript": "^4.9.5"
38
+ "typescript": "^5.2.2"
39
39
  },
40
40
  "engines": {
41
41
  "node": ">=16.13"
@@ -1227,7 +1227,7 @@ export type CollectionQuery = {
1227
1227
  >;
1228
1228
  pageInfo: Pick<
1229
1229
  StorefrontAPI.PageInfo,
1230
- 'hasPreviousPage' | 'hasNextPage' | 'endCursor'
1230
+ 'hasPreviousPage' | 'hasNextPage' | 'endCursor' | 'startCursor'
1231
1231
  >;
1232
1232
  };
1233
1233
  }
@@ -1819,7 +1819,7 @@ interface GeneratedQueryTypes {
1819
1819
  return: BlogsQuery;
1820
1820
  variables: BlogsQueryVariables;
1821
1821
  };
1822
- '#graphql\n #graphql\n fragment MoneyProductItem on MoneyV2 {\n amount\n currencyCode\n }\n fragment ProductItem on Product {\n id\n handle\n title\n featuredImage {\n id\n altText\n url\n width\n height\n }\n priceRange {\n minVariantPrice {\n ...MoneyProductItem\n }\n maxVariantPrice {\n ...MoneyProductItem\n }\n }\n variants(first: 1) {\n nodes {\n selectedOptions {\n name\n value\n }\n }\n }\n }\n\n query Collection(\n $handle: String!\n $country: CountryCode\n $language: LanguageCode\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n ) @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id\n handle\n title\n description\n products(\n first: $first,\n last: $last,\n before: $startCursor,\n after: $endCursor\n ) {\n nodes {\n ...ProductItem\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n hasNextPage\n endCursor\n }\n }\n }\n }\n': {
1822
+ '#graphql\n #graphql\n fragment MoneyProductItem on MoneyV2 {\n amount\n currencyCode\n }\n fragment ProductItem on Product {\n id\n handle\n title\n featuredImage {\n id\n altText\n url\n width\n height\n }\n priceRange {\n minVariantPrice {\n ...MoneyProductItem\n }\n maxVariantPrice {\n ...MoneyProductItem\n }\n }\n variants(first: 1) {\n nodes {\n selectedOptions {\n name\n value\n }\n }\n }\n }\n\n query Collection(\n $handle: String!\n $country: CountryCode\n $language: LanguageCode\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n ) @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id\n handle\n title\n description\n products(\n first: $first,\n last: $last,\n before: $startCursor,\n after: $endCursor\n ) {\n nodes {\n ...ProductItem\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n endCursor\n startCursor\n }\n }\n }\n }\n': {
1823
1823
  return: CollectionQuery;
1824
1824
  variables: CollectionQueryVariables;
1825
1825
  };
@@ -7,6 +7,7 @@
7
7
  "jsx": "react-jsx",
8
8
  "moduleResolution": "node",
9
9
  "resolveJsonModule": true,
10
+ "module": "ES2022",
10
11
  "target": "ES2022",
11
12
  "strict": true,
12
13
  "allowJs": true,
package/dist/lib/file.js CHANGED
@@ -8,7 +8,7 @@ async function replaceFileContent(filepath, formatConfig, replacer) {
8
8
  if (typeof content !== "string")
9
9
  return;
10
10
  if (formatConfig) {
11
- content = formatCode(content, formatConfig, filepath);
11
+ content = await formatCode(content, formatConfig, filepath);
12
12
  }
13
13
  return writeFile(filepath, content);
14
14
  }
package/dist/lib/flags.js CHANGED
@@ -3,7 +3,7 @@ import { camelize } from '@shopify/cli-kit/common/string';
3
3
  import { renderInfo } from '@shopify/cli-kit/node/ui';
4
4
  import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
5
5
  import colors from '@shopify/cli-kit/node/colors';
6
- import { SETUP_CSS_STRATEGIES } from './setups/css/index.js';
6
+ import { STYLING_CHOICES } from './setups/css/index.js';
7
7
  import { I18N_CHOICES } from './setups/i18n/index.js';
8
8
 
9
9
  const DEFAULT_PORT = 3e3;
@@ -50,10 +50,10 @@ const commonFlags = {
50
50
  dependsOn: ["codegen-unstable"]
51
51
  }),
52
52
  styling: Flags.string({
53
- description: `Sets the styling strategy to use. One of ${SETUP_CSS_STRATEGIES.map(
53
+ description: `Sets the styling strategy to use. One of ${STYLING_CHOICES.map(
54
54
  (item) => `\`${item}\``
55
55
  ).join(", ")}.`,
56
- choices: SETUP_CSS_STRATEGIES,
56
+ choices: STYLING_CHOICES,
57
57
  env: "SHOPIFY_HYDROGEN_FLAG_STYLING"
58
58
  }),
59
59
  markets: Flags.string({
@@ -1,4 +1,3 @@
1
- import prettier from 'prettier';
2
1
  import { extname } from '@shopify/cli-kit/node/path';
3
2
 
4
3
  const DEFAULT_PRETTIER_CONFIG = {
@@ -9,21 +8,22 @@ const DEFAULT_PRETTIER_CONFIG = {
9
8
  };
10
9
  async function getCodeFormatOptions(filePath = process.cwd()) {
11
10
  try {
11
+ const prettier = await import('prettier');
12
12
  return await prettier.resolveConfig(filePath) || DEFAULT_PRETTIER_CONFIG;
13
13
  } catch {
14
14
  return DEFAULT_PRETTIER_CONFIG;
15
15
  }
16
16
  }
17
- function formatCode(content, config = DEFAULT_PRETTIER_CONFIG, filePath = "") {
17
+ async function formatCode(content, config = DEFAULT_PRETTIER_CONFIG, filePath = "") {
18
18
  const ext = extname(filePath);
19
- const formattedContent = prettier.format(content, {
19
+ const prettier = await import('prettier');
20
+ return prettier.format(content, {
20
21
  // Specify the TypeScript parser for ts/tsx files. Otherwise
21
22
  // we need to use the babel parser because the default parser
22
23
  // Otherwise prettier will print a warning.
23
24
  parser: ext === ".tsx" || ext === ".ts" ? "typescript" : "babel",
24
25
  ...config
25
26
  });
26
- return formattedContent;
27
27
  }
28
28
 
29
29
  export { formatCode, getCodeFormatOptions };
@@ -20,6 +20,24 @@ async function adminRequest(query, session, variables) {
20
20
  "Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen"
21
21
  );
22
22
  }
23
+ if (errors?.some?.(
24
+ (error2) => error2.message.includes(
25
+ "Access denied for hydrogenStorefrontCreate field"
26
+ )
27
+ )) {
28
+ throw new AbortError("Couldn't connect storefront to Shopify", [
29
+ "Common reasons for this error include:",
30
+ {
31
+ list: {
32
+ items: [
33
+ "The Hydrogen sales channel isn't installed on the store.",
34
+ "You don't have the required account permission to manage apps or channels.",
35
+ "You're trying to connect to an ineligible store type (Trial, Development store)"
36
+ ]
37
+ }
38
+ }
39
+ ]);
40
+ }
23
41
  throw error;
24
42
  }
25
43
  }
@@ -16,8 +16,8 @@ describe("adminRequest", () => {
16
16
  });
17
17
  expect(response).toContain(fakeResponse);
18
18
  });
19
- describe("error response", () => {
20
- it("sends a query to the Admin API and returns an unknown error response", async () => {
19
+ describe("when there is an unknown error response", () => {
20
+ it("passes along the error message", async () => {
21
21
  const fakeGraphqlError = {
22
22
  errors: [
23
23
  {
@@ -32,7 +32,9 @@ describe("adminRequest", () => {
32
32
  });
33
33
  await expect(response).rejects.toContain(fakeGraphqlError);
34
34
  });
35
- it("sends a query to the Admin API and returns an error where app isn't installed", async () => {
35
+ });
36
+ describe("when the app isn't installed", () => {
37
+ it("throws an AbortError", async () => {
36
38
  const fakeGraphqlError = {
37
39
  errors: [
38
40
  {
@@ -46,6 +48,29 @@ describe("adminRequest", () => {
46
48
  storeFqdn: ""
47
49
  });
48
50
  await expect(response).rejects.toThrowError(AbortError);
51
+ await expect(response).rejects.toMatch(
52
+ /Hydrogen sales channel isn\'t installed/
53
+ );
54
+ });
55
+ });
56
+ describe("when the user doesn't have access to hydrogenStorefrontCreate", () => {
57
+ it("throws an AbortError", async () => {
58
+ const fakeGraphqlError = {
59
+ errors: [
60
+ {
61
+ message: "Access denied for hydrogenStorefrontCreate field"
62
+ }
63
+ ]
64
+ };
65
+ vi.mocked(graphqlRequest).mockRejectedValue(fakeGraphqlError);
66
+ const response = adminRequest("", {
67
+ token: "",
68
+ storeFqdn: ""
69
+ });
70
+ await expect(response).rejects.toThrowError(AbortError);
71
+ await expect(response).rejects.toMatch(
72
+ /Couldn\'t connect storefront to Shopify/
73
+ );
49
74
  });
50
75
  });
51
76
  });
@@ -18,6 +18,7 @@ async function startMiniOxygen({
18
18
  const dotenvPath = resolvePath(root, ".env");
19
19
  const miniOxygen = await startServer({
20
20
  script: await readFile(buildPathWorkerFile),
21
+ workerFile: buildPathWorkerFile,
21
22
  assetsDir: buildPathClient,
22
23
  publicPath: "",
23
24
  port,
@@ -1,10 +1,12 @@
1
1
  import { renderSelectPrompt } from '@shopify/cli-kit/node/ui';
2
+ import { SETUP_CSS_STRATEGIES } from './assets.js';
3
+ export { SETUP_CSS_STRATEGIES } from './assets.js';
2
4
  import { setupTailwind } from './tailwind.js';
3
5
  import { setupPostCss } from './postcss.js';
4
6
  import { setupCssModules } from './css-modules.js';
5
7
  import { setupVanillaExtract } from './vanilla-extract.js';
6
- export { SETUP_CSS_STRATEGIES } from './assets.js';
7
8
 
9
+ const STYLING_CHOICES = [...SETUP_CSS_STRATEGIES, "none"];
8
10
  const CSS_STRATEGY_NAME_MAP = {
9
11
  tailwind: "Tailwind",
10
12
  "css-modules": "CSS Modules",
@@ -41,4 +43,4 @@ async function renderCssPrompt(options) {
41
43
  });
42
44
  }
43
45
 
44
- export { CSS_STRATEGY_NAME_MAP, renderCssPrompt, setupCssStrategy };
46
+ export { CSS_STRATEGY_NAME_MAP, STYLING_CHOICES, renderCssPrompt, setupCssStrategy };
@@ -64,8 +64,7 @@ async function generateRoutes(options) {
64
64
  options,
65
65
  v2Flags.isV2RouteConvention
66
66
  );
67
- const typescript = options.typescript ?? !!tsconfigPath;
68
- const transpilerOptions = typescript ? void 0 : await getJsTranspilerOptions(rootDirectory);
67
+ const typescript = !!(options.typescript ?? tsconfigPath?.endsWith("tsconfig.json"));
69
68
  const routes = [];
70
69
  for (const route of routesArray) {
71
70
  routes.push(
@@ -76,7 +75,6 @@ async function generateRoutes(options) {
76
75
  rootDirectory,
77
76
  appDirectory,
78
77
  formatOptions,
79
- transpilerOptions,
80
78
  v2Flags
81
79
  })
82
80
  );
@@ -85,7 +83,6 @@ async function generateRoutes(options) {
85
83
  routes,
86
84
  routeGroups,
87
85
  isTypescript: typescript,
88
- transpilerOptions,
89
86
  v2Flags,
90
87
  formatOptions
91
88
  };
@@ -98,12 +95,12 @@ async function getLocalePrefix(appDirectory, { localePrefix, routeName }, isV2Ro
98
95
  const existingFiles = await readdir(joinPath(appDirectory, "routes")).catch(
99
96
  () => []
100
97
  );
101
- const homeRouteWithLocaleRE = isV2RouteConvention ? /^\(\$(\w+)\)\._index.[jt]sx?$/ : /^\(\$(\w+)\)$/;
102
- const homeRouteWithLocale = existingFiles.find(
103
- (file) => homeRouteWithLocaleRE.test(file)
98
+ const coreRouteWithLocaleRE = isV2RouteConvention ? /^\(\$(\w+)\)\.(_index|\$|cart).[jt]sx?$/ : /^\(\$(\w+)\)$/;
99
+ const coreRouteWithLocale = existingFiles.find(
100
+ (file) => coreRouteWithLocaleRE.test(file)
104
101
  );
105
- if (homeRouteWithLocale) {
106
- return homeRouteWithLocale.match(homeRouteWithLocaleRE)?.[1];
102
+ if (coreRouteWithLocale) {
103
+ return coreRouteWithLocale.match(coreRouteWithLocaleRE)?.[1];
107
104
  }
108
105
  }
109
106
  async function generateProjectFile(routeFrom, {
@@ -113,7 +110,6 @@ async function generateProjectFile(routeFrom, {
113
110
  force,
114
111
  adapter,
115
112
  templatesRoot = getStarterDir(),
116
- transpilerOptions,
117
113
  formatOptions,
118
114
  localePrefix,
119
115
  v2Flags = {},
@@ -159,19 +155,17 @@ async function generateProjectFile(routeFrom, {
159
155
  if (!await fileExists(dirname(destinationPath))) {
160
156
  await mkdir(dirname(destinationPath));
161
157
  }
158
+ const templateAppFilePath = getTemplateAppFile(filePath, templatesRoot);
162
159
  if (!/\.[jt]sx?$/.test(filePath)) {
163
- await copyFile(
164
- getTemplateAppFile(filePath, templatesRoot),
165
- destinationPath
166
- );
160
+ await copyFile(templateAppFilePath, destinationPath);
167
161
  continue;
168
162
  }
169
163
  let templateContent = convertTemplateToRemixVersion(
170
- await readFile(getTemplateAppFile(filePath, templatesRoot)),
164
+ await readFile(templateAppFilePath),
171
165
  v2Flags
172
166
  );
173
167
  if (!typescript) {
174
- templateContent = transpileFile(templateContent, transpilerOptions);
168
+ templateContent = await transpileFile(templateContent);
175
169
  }
176
170
  if (adapter) {
177
171
  templateContent = templateContent.replace(
@@ -179,7 +173,7 @@ async function generateProjectFile(routeFrom, {
179
173
  adapter
180
174
  );
181
175
  }
182
- templateContent = formatCode(
176
+ templateContent = await formatCode(
183
177
  templateContent,
184
178
  formatOptions,
185
179
  destinationPath
@@ -223,17 +217,6 @@ async function findRouteDependencies(routeFilePath, appDirectory) {
223
217
  }
224
218
  return [...fileDependencies];
225
219
  }
226
- async function getJsTranspilerOptions(rootDirectory) {
227
- const jsConfigPath = joinPath(rootDirectory, "jsconfig.json");
228
- if (!await fileExists(jsConfigPath))
229
- return;
230
- return JSON.parse(
231
- (await readFile(jsConfigPath, { encoding: "utf8" })).replace(
232
- /^\s*\/\/.*$/gm,
233
- ""
234
- )
235
- )?.compilerOptions;
236
- }
237
220
  async function renderRoutePrompt(options) {
238
221
  const generateAll = await renderConfirmationPrompt({
239
222
  message: "Scaffold all standard route files? " + Object.keys(ROUTE_MAP).join(", "),
@@ -39,7 +39,6 @@ describe("generate/route", () => {
39
39
  expect(result).toMatchObject(
40
40
  expect.objectContaining({
41
41
  isTypescript: false,
42
- transpilerOptions: { test: "js" },
43
42
  formatOptions: { singleQuote: false },
44
43
  routes: expect.any(Array)
45
44
  })
@@ -61,7 +60,7 @@ describe("generate/route", () => {
61
60
  });
62
61
  vi.mocked(getRemixConfig).mockResolvedValue({
63
62
  ...directories,
64
- tsconfigPath: "somewhere",
63
+ tsconfigPath: "somewhere/tsconfig.json",
65
64
  future: {
66
65
  v2_routeConvention: true
67
66
  }
@@ -74,7 +73,6 @@ describe("generate/route", () => {
74
73
  expect(result).toMatchObject(
75
74
  expect.objectContaining({
76
75
  isTypescript: true,
77
- transpilerOptions: void 0,
78
76
  routes: expect.any(Array),
79
77
  formatOptions: expect.any(Object)
80
78
  })
@@ -1,6 +1,5 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs/promises';
3
- import ts from 'typescript';
4
3
  import glob from 'fast-glob';
5
4
  import { outputDebug } from '@shopify/cli-kit/node/output';
6
5
  import { getCodeFormatOptions, formatCode } from './format-code.js';
@@ -18,7 +17,9 @@ const DEFAULT_TS_CONFIG = {
18
17
  forceConsistentCasingInFileNames: true,
19
18
  skipLibCheck: true
20
19
  };
21
- function transpileFile(code, config = DEFAULT_TS_CONFIG) {
20
+ async function transpileFile(code, config = DEFAULT_TS_CONFIG) {
21
+ const tsImport = await import('typescript');
22
+ const ts = tsImport.default ?? tsImport;
22
23
  const withArtificialNewLines = escapeNewLines(code);
23
24
  const compiled = ts.transpileModule(withArtificialNewLines, {
24
25
  reportDiagnostics: false,
@@ -82,7 +83,7 @@ async function transpileProject(projectDir) {
82
83
  continue;
83
84
  }
84
85
  const tsx = await fs.readFile(entry, "utf8");
85
- const mjs = formatCode(transpileFile(tsx), formatConfig);
86
+ const mjs = await formatCode(await transpileFile(tsx), formatConfig);
86
87
  await fs.rm(entry);
87
88
  await fs.writeFile(entry.replace(/\.ts(x?)$/, ".js$1"), mjs, "utf8");
88
89
  }
@@ -1,28 +1,31 @@
1
- const HydrogenLogoBaseBW = (props) => /* @__PURE__ */ React.createElement(
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ const HydrogenLogoBaseBW = (props) => /* @__PURE__ */ jsxs(
2
3
  "svg",
3
4
  {
4
5
  width: 81,
5
6
  height: 82,
6
7
  fill: "none",
7
8
  xmlns: "http://www.w3.org/2000/svg",
8
- ...props
9
- },
10
- /* @__PURE__ */ React.createElement(
11
- "path",
12
- {
13
- d: "M39.955 81.28 2.138 61.19l12.933-6.818 14.562 7.733 12.218-6.441L27.29 47.93l12.933-6.833L78.04 61.189l-12.934 6.817L51.35 60.7l-12.236 6.457 13.774 7.308-12.933 6.817Z",
14
- fill: "#000"
15
- }
16
- ),
17
- /* @__PURE__ */ React.createElement(
18
- "path",
19
- {
20
- fillRule: "evenodd",
21
- clipRule: "evenodd",
22
- d: "m40.225 0 39.953 21.227-15.073 7.945-13.756-7.308-10.096 5.328 13.775 7.309-15.075 7.945L0 21.22l15.073-7.945 14.562 7.732 10.078-5.313-14.56-7.731L40.225 0ZM29.426 7.967l14.564 7.734L29.63 23.27 15.07 15.537l-10.794 5.69 35.68 18.956 10.793-5.688-13.773-7.307L51.352 19.6l13.757 7.308 10.794-5.69-35.68-18.956-10.797 5.704Z",
23
- fill: "#000"
24
- }
25
- )
9
+ ...props,
10
+ children: [
11
+ /* @__PURE__ */ jsx(
12
+ "path",
13
+ {
14
+ d: "M39.955 81.28 2.138 61.19l12.933-6.818 14.562 7.733 12.218-6.441L27.29 47.93l12.933-6.833L78.04 61.189l-12.934 6.817L51.35 60.7l-12.236 6.457 13.774 7.308-12.933 6.817Z",
15
+ fill: "#000"
16
+ }
17
+ ),
18
+ /* @__PURE__ */ jsx(
19
+ "path",
20
+ {
21
+ fillRule: "evenodd",
22
+ clipRule: "evenodd",
23
+ d: "m40.225 0 39.953 21.227-15.073 7.945-13.756-7.308-10.096 5.328 13.775 7.309-15.075 7.945L0 21.22l15.073-7.945 14.562 7.732 10.078-5.313-14.56-7.731L40.225 0ZM29.426 7.967l14.564 7.734L29.63 23.27 15.07 15.537l-10.794 5.69 35.68 18.956 10.793-5.688-13.773-7.307L51.352 19.6l13.757 7.308 10.794-5.69-35.68-18.956-10.797 5.704Z",
24
+ fill: "#000"
25
+ }
26
+ )
27
+ ]
28
+ }
26
29
  );
27
30
  export {
28
31
  HydrogenLogoBaseBW