Compare commits

...

33 Commits

Author SHA1 Message Date
Ethan Arrowood
74593e4d81 Publish Stable
- @vercel/build-utils@5.5.8
 - vercel@28.5.2
 - @vercel/client@12.2.18
 - @vercel/go@2.2.16
 - @vercel/hydrogen@0.0.30
 - @vercel/next@3.2.12
 - @vercel/node@2.6.3
 - @vercel/python@3.1.26
 - @vercel/redwood@1.0.36
 - @vercel/remix@1.0.36
 - @vercel/ruby@1.3.42
 - @vercel/static-build@1.0.37
2022-11-15 14:56:46 -07:00
Ethan Arrowood
3770b84999 [cli] add support for JSON5 and skip when no rootDirectory (#8913)
### Related Issues

Adds support for comments in JSON configs and skips operation when root directory is null or `'.'`

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-15 21:00:40 +00:00
Nathan Rajlich
91bc2e693b [cli] Remove readOutputStream() helper function for tests (#8889)
The `readOutputStream()` module was counting "data" events, not lines specifically, and was failing for me locally. So that was removed, and updated the tests that were using it to use the `line-async-iterator` module instead to be more explicit and be able to perform assertions on a per-line basis.
2022-11-15 19:37:18 +00:00
Nathan Rajlich
898a5621f1 [build-utils] Fix runNpmInstall() error when install fails (#8909)
Fixes a regression where the thrown error was not propagated to the caller when npm deps failed to install.
2022-11-15 08:25:21 +00:00
Sean Massa
4e41c0e0a6 Publish Stable
- vercel@28.5.1
 - @vercel/fs-detectors@3.5.1
2022-11-14 14:24:34 -06:00
Ethan Arrowood
a8822170d6 [fs-detectors] restore rush support in fs-detectors (#8907)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-11-14 14:23:12 -06:00
Sean Massa
7d87f66f5d [cli] update yarn.lock (#8905) 2022-11-14 14:07:50 -06:00
Sean Massa
8d2c0fec89 Publish Stable
- vercel@28.5.0
 - @vercel/edge@0.1.2
 - @vercel/fs-detectors@3.5.0
 - @vercel/python@3.1.25
2022-11-14 12:53:42 -06:00
Ethan Arrowood
0663524cd7 [cli][fs-detectors] Improve vc build monorepo support (#8742)
### Related Issues

Improves how `vc build` handles monorepos. In short, this introduces
monorepo manager detection logic and then some helpful defaults so users
don't have to manually specify a `buildCommand` or `installCommand` when
linking a project within a monorepo.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-11-14 10:23:33 -07:00
Chris Barber
f283b3b106 [cli] Add node_modules/.bin to PATH instead of running npx/yarn run (#8890)
`runDevCommand()` assumes the dev command is an npm package and thus uses `npx` or `yarn run` to execute it. In the case of a Hugo-based app, there is no npm package, so we want spawn to find Hugo in the `PATH`. Then for Node-based apps, instead of `npx`, spawn should find the command since `node_modules/.bin` has been added to the `PATH`.

### Related Issues

> https://github.com/vercel/customer-issues/issues/871

Note: This PR is a recreation of https://github.com/vercel/vercel/pull/8864 because prettier changed a bunch of Hugo files, which was bloating the original PR.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-14 15:26:27 +00:00
github-actions[bot]
b572ef5d71 [examples] Upgrade Next.js to version 13.0.3 (#8891) 2022-11-11 14:38:57 -08:00
Chris Barber
71e233ce7f [python] Explicitly bind HTTP server to localhost (#8880)
On Windows, Python 3 is unable to listen on `0.0.0.0` and errors with:

```
[WinError 10049] The requested address is not valid in its context
```

The only solution I found is to bind to `127.0.0.1` or `localhost`. I've run into issues before with using `localhost`, so I chose to go with `127.0.0.1` and Windows (and macOS) seems to be happy.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-11 02:33:31 +00:00
Nathan Rajlich
5669fad6bc [cli] Remove --runInBand from Jest (#8888) 2022-11-11 02:25:38 +00:00
Dominik Ferber
c294409f5b [edge] fix broken link on npmjs.org (#8848)
Before this change clicking on "follow the documentation" from
https://www.npmjs.com/package/@vercel/edge would lead to a 404 as the
docs link is relative.
2022-11-10 13:30:27 -08:00
Andy McKay
686f78a86e [cli] Add in a message for deployment-error message (#8865)
We have a link to an error page here:
https://github.com/vercel/vercel/blob/main/packages/cli/src/commands/deploy/index.ts#L873,
however that page is currently a 404.

This is an attempt to explain the error a bit more. Because there are
many possible causes of a deployment error, this mostly focuses on
explaining how to get to the logs.

### Related Issues

Fixes https://github.com/vercel/vercel/issues/1732. 

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Chris Barber <chris.barber@vercel.com>
2022-11-10 11:40:57 -06:00
Josh Thomas
c6ed021d2e [examples] Add Vercel Analytics to Astro template (#8472)
Co-authored-by: Lee Robinson <lrobinson2011@gmail.com>
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-11-10 09:24:20 -06:00
Ryan Carniato
f8cdc943ca [examples] update SolidStart example (#8707)
Updates the SolidStart example to the latest in anticipation of the
upcoming beta release.
2022-11-09 23:27:24 -08:00
Nathan Rajlich
d8d30b59ec [examples] Remove ENABLE_VC_BUILD env var from Solidstart template (#8875)
Env var is no longer required.
2022-11-09 00:43:42 -08:00
Nathan Rajlich
a93383cf2e Remove ENABLE_VC_BUILD from Vercel deployment (#8876)
This env var is no longer necessary.
2022-11-09 07:29:05 +00:00
Lee Robinson
49dac4c229 [examples] Update Remix template for latest. (#8857)
From `create-remix`
2022-11-08 22:22:58 -06:00
JJ Kasper
1b211f28df Publish Stable
- vercel@28.4.17
 - @vercel/client@12.2.17
 - @vercel/frameworks@1.1.12
 - @vercel/fs-detectors@3.4.9
 - @vercel/next@3.2.11
 - @vercel/redwood@1.0.35
 - @vercel/routing-utils@2.1.3
 - @vercel/static-build@1.0.36
2022-11-08 16:21:25 -08:00
JJ Kasper
c2d0887b94 [next][routing-utils] Add missing matcher support (#8874)
### Related Issues

This adds handling to ensure we pass through the `missing` route field
correctly for custom routes and middleware matchers. Tests are also
added in the complimentary Next.js PR for this, example deployment can
be seen here
https://vtest314-e2e-tests-jj4-vtest314-next-e2e-tests.vercel.app/

x-ref: [slack
thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1667935428788529?thread_ts=1667850697.542269&cid=C03S8ED1DKM)
x-ref: https://github.com/vercel/next.js/pull/42660

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-08 16:17:34 -08:00
JJ Kasper
fbb8bba4cf Publish Stable
- vercel@28.4.16
 - @vercel/next@3.2.10
2022-11-08 11:03:01 -08:00
JJ Kasper
56cc87fe9d [next] Handle skip normalize flag for middleware (#8873)
### Related Issues

x-ref: [slack
thread](https://vercel.slack.com/archives/C01224Q5M99/p1667927545637489?thread_ts=1664536480.045539&cid=C01224Q5M99)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-08 10:31:02 -08:00
JJ Kasper
0027ffa65b [next] Ensure rsc dynamic routes are correct (#8866)
### Related Issues

This ensures we properly route dynamic routes with segments in the
middle of the path for rsc paths and also ensures we normalize the
header values from the routes-manifest correctly.

x-ref: https://github.com/vercel/next.js/issues/42364
x-ref:
https://github.com/vercel/next.js/actions/runs/3415662540/jobs/5685787894

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-08 07:24:32 -08:00
JJ Kasper
0964be1710 Publish Stable
- vercel@28.4.15
 - @vercel/client@12.2.16
 - @vercel/error-utils@1.0.3
 - @vercel/frameworks@1.1.11
 - @vercel/fs-detectors@3.4.8
 - @vercel/hydrogen@0.0.29
 - @vercel/next@3.2.9
 - @vercel/node-bridge@3.1.2
 - @vercel/node@2.6.2
 - @vercel/redwood@1.0.34
 - @vercel/remix@1.0.35
 - @vercel/routing-utils@2.1.2
 - @vercel/static-build@1.0.35
 - @vercel/static-config@2.0.6
2022-11-07 12:46:03 -08:00
JJ Kasper
9618ffe05f [next] Update rsc header handling (#8862)
### Related Issues

Required for https://github.com/vercel/next.js/pull/42482

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-07 12:42:26 -08:00
github-actions[bot]
832ba3fe23 [examples] Upgrade Next.js to version 13.0.2 (#8847)
This auto-generated PR updates Next.js to version 13.0.2
2022-11-04 16:18:30 -07:00
Ethan Arrowood
253b4fd1d2 [cli][client][error-utils][frameworks][fs-detectors][hydrogen][next][node-bridge][node][redwood][remix][routing-utils][static-config] update @types/node to v14 across repo (#8842)
### Related Issues

Updates @types/node to the latest version within the v14 major (based on `npm view @types/node`)

```
❯ npm view @types/node@'>=14.0.0 <15.0.0' version | tail -1
@types/node@14.18.33 '14.18.33'
```

This PR also fixes the various necessary type changes

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-04 20:21:13 +00:00
Nathan Rajlich
9e5f17b3c6 [cli] Allow vc link to overwrite existing link (#8845)
There was a regression in #8670 which caused `vc link` to be a no-op
when there was already a link to a Vercel project in the `.vercel`
directory.
2022-11-04 12:12:37 -07:00
Ethan Arrowood
39d0f8dbfc [cli] fix broken integration test (#8844)
### Related Issues

https://github.com/vercel/api/pull/15009 broke one of our integration tests. This PR fixes it.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-11-04 00:12:56 +00:00
Andy McKay
a567d047d1 Publish Stable
- @vercel/build-utils@5.5.7
 - vercel@28.4.14
 - @vercel/client@12.2.15
 - @vercel/edge@0.1.1
 - @vercel/error-utils@1.0.2
 - @vercel/frameworks@1.1.10
 - @vercel/fs-detectors@3.4.7
 - @vercel/go@2.2.15
 - @vercel/hydrogen@0.0.28
 - @vercel/next@3.2.8
 - @vercel/node-bridge@3.1.1
 - @vercel/node@2.6.1
 - @vercel/python@3.1.24
 - @vercel/redwood@1.0.33
 - @vercel/remix@1.0.34
 - @vercel/routing-utils@2.1.1
 - @vercel/ruby@1.3.41
 - @vercel/static-build@1.0.34
 - @vercel/static-config@2.0.5
2022-11-02 13:14:37 -07:00
Steven
fdfb3a385e [error-utils] Allow publishing to npm (#8839)
> If you set "private": true in your package.json, then npm will refuse
to publish it.


https://docs.npmjs.com/cli/v8/configuring-npm/package-json#private
2022-11-02 16:12:09 -04:00
178 changed files with 32214 additions and 1935 deletions

View File

@@ -16,7 +16,7 @@
"unzip-stream": "0.3.0"
},
"devDependencies": {
"@types/node": "13.1.4",
"@types/node": "14.18.33",
"@types/node-fetch": "2.5.4",
"@vercel/node": "1.9.0",
"typescript": "3.9.6"

View File

@@ -0,0 +1,13 @@
# Deployment failed
## Why This Error Occurred
When deploying this project, there was not a successful deployment into the `READY` state.
## How to Fix It
This is a generic error to catch problems in the deployment. The error is likely to vary depending on the deployment and the conditions at the time.
Try looking in the logs for information about the deployment and the failure, this could be done at (vercel.com)[https://vercel.com]. You can also use the `vc logs` command to display the build logs for the deployment.
This might not be a permanent error and retrying the deployment might also resolve it.

View File

@@ -8,6 +8,7 @@
"astro": "astro"
},
"devDependencies": {
"astro": "^1.0.0-rc.8"
"astro": "^1.0.0-rc.8",
"web-vitals": "^3.0.0"
}
}

View File

@@ -1 +1,9 @@
/// <reference types="astro/client" />
interface ImportMetaEnv {
readonly PUBLIC_VERCEL_ANALYTICS_ID: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -14,6 +14,19 @@ const { title } = Astro.props as Props;
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<script>
import { webVitals } from "../lib/vitals";
let analyticsId = import.meta.env.PUBLIC_VERCEL_ANALYTICS_ID;
if (analyticsId) {
webVitals({
path: location.pathname,
params: location.search,
analyticsId,
});
}
</script>
</head>
<body>
<slot />

View File

@@ -0,0 +1,66 @@
import { onCLS, onFCP, onFID, onLCP, onTTFB } from "web-vitals";
const vitalsUrl = "https://vitals.vercel-analytics.com/v1/vitals";
function getConnectionSpeed() {
return "connection" in navigator &&
navigator["connection"] &&
"effectiveType" in navigator["connection"]
? // @ts-ignore
navigator["connection"]["effectiveType"]
: "";
}
/**
* @param {import("web-vitals").Metric} metric
* @param {{ params: { [s: string]: any; } | ArrayLike<any>; path: string; analyticsId: string; debug: boolean; }} options
*/
export function sendToAnalytics(metric, options) {
const page = Object.entries(options.params).reduce(
(acc, [key, value]) => acc.replace(value, `[${key}]`),
options.path
);
const body = {
dsn: options.analyticsId,
id: metric.id,
page,
href: location.href,
event_name: metric.name,
value: metric.value.toString(),
speed: getConnectionSpeed(),
};
if (options.debug) {
console.log("[Analytics]", metric.name, JSON.stringify(body, null, 2));
}
const blob = new Blob([new URLSearchParams(body).toString()], {
// This content type is necessary for `sendBeacon`
type: "application/x-www-form-urlencoded",
});
if (navigator.sendBeacon) {
navigator.sendBeacon(vitalsUrl, blob);
} else
fetch(vitalsUrl, {
body: blob,
method: "POST",
credentials: "omit",
keepalive: true,
});
}
/**
* @param {any} options
*/
export function webVitals(options) {
try {
onFID((metric) => sendToAnalytics(metric, options));
onTTFB((metric) => sendToAnalytics(metric, options));
onLCP((metric) => sendToAnalytics(metric, options));
onCLS((metric) => sendToAnalytics(metric, options));
onFCP((metric) => sendToAnalytics(metric, options));
} catch (err) {
console.error("[Analytics]", err);
}
}

View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"next": "13.0.1",
"eslint": "8.27.0",
"eslint-config-next": "13.0.3",
"next": "13.0.3",
"react": "18.2.0",
"react-dom": "18.2.0"
}

View File

@@ -8,6 +8,13 @@ This directory is a brief example of a [Remix](https://remix.run/docs) site that
_Live Example: https://remix-run-template.vercel.app_
You can also deploy using the [Vercel CLI](https://vercel.com/cli):
```sh
npm i -g vercel
vercel
```
## Development
To run your Remix app locally, make sure your project's local dependencies are installed:
@@ -23,5 +30,3 @@ npm run dev
```
Open up [http://localhost:3000](http://localhost:3000) and you should be ready to go!
If you're used to using the `vercel dev` command provided by [Vercel CLI](https://vercel.com/cli) instead, you can also use that, but it's not needed.

View File

@@ -1,4 +1,22 @@
import { RemixBrowser } from "@remix-run/react";
import { hydrate } from "react-dom";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
hydrate(<RemixBrowser />, document);
function hydrate() {
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>
);
});
}
if (window.requestIdleCallback) {
window.requestIdleCallback(hydrate);
} else {
// Safari doesn't support requestIdleCallback
// https://caniuse.com/requestidlecallback
window.setTimeout(hydrate, 1);
}

View File

@@ -8,14 +8,14 @@ export default function handleRequest(
responseHeaders: Headers,
remixContext: EntryContext
) {
let markup = renderToString(
const markup = renderToString(
<RemixServer context={remixContext} url={request.url} />
);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders,
status: responseStatusCode,
});
}

View File

@@ -1,182 +1,34 @@
import type { LinksFunction, MetaFunction } from "@remix-run/node";
import type { MetaFunction } from "@remix-run/node";
import {
Link,
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useCatch,
} from "@remix-run/react";
import { Analytics } from '@vercel/analytics/react';
import darkStylesUrl from "~/styles/dark.css";
import globalStylesUrl from "~/styles/global.css";
// https://remix.run/api/conventions#links
export let links: LinksFunction = () => {
return [
{ rel: "stylesheet", href: globalStylesUrl },
{
rel: "stylesheet",
href: darkStylesUrl,
media: "(prefers-color-scheme: dark)"
}
];
};
// https://remix.run/api/conventions#meta
export let meta: MetaFunction = () => ({
export const meta: MetaFunction = () => ({
charset: "utf-8",
title: "New Remix App",
viewport: "width=device-width,initial-scale=1",
});
// https://remix.run/api/conventions#default-export
// https://remix.run/api/conventions#route-filenames
export default function App() {
return (
<Document>
<Layout>
<Outlet />
</Layout>
</Document>
);
}
// https://remix.run/api/conventions#errorboundary
export function ErrorBoundary({ error }: { error: Error }) {
console.error(error);
return (
<Document title="Error!">
<Layout>
<div>
<h1>There was an error</h1>
<p>{error.message}</p>
<hr />
<p>
Hey, developer, you should replace this with what you want your
users to see.
</p>
</div>
</Layout>
</Document>
);
}
// https://remix.run/api/conventions#catchboundary
export function CatchBoundary() {
let caught = useCatch();
let message;
switch (caught.status) {
case 401:
message = (
<p>
Oops! Looks like you tried to visit a page that you do not have access
to.
</p>
);
break;
case 404:
message = (
<p>Oops! Looks like you tried to visit a page that does not exist.</p>
);
break;
default:
throw new Error(caught.data || caught.statusText);
}
return (
<Document title={`${caught.status} ${caught.statusText}`}>
<Layout>
<h1>
{caught.status}: {caught.statusText}
</h1>
{message}
</Layout>
</Document>
);
}
function Document({
children,
title
}: {
children: React.ReactNode;
title?: string;
}) {
return (
<html lang="en">
<head>
{title ? <title>{title}</title> : null}
<Meta />
<Links />
</head>
<body>
{children}
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
<Analytics />
</body>
</html>
);
}
function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="remix-app">
<header className="remix-app__header">
<div className="container remix-app__header-content">
<Link to="/" title="Remix" className="remix-app__header-home-link">
<RemixLogo />
</Link>
<nav aria-label="Main navigation" className="remix-app__header-nav">
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<a href="https://remix.run/docs">Remix Docs</a>
</li>
<li>
<a href="https://github.com/remix-run/remix">GitHub</a>
</li>
</ul>
</nav>
</div>
</header>
<div className="remix-app__main">
<div className="container remix-app__main-content">{children}</div>
</div>
<footer className="remix-app__footer">
<div className="container remix-app__footer-content">
<p>&copy; You!</p>
</div>
</footer>
</div>
);
}
function RemixLogo() {
return (
<svg
viewBox="0 0 659 165"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
aria-labelledby="remix-run-logo-title"
role="img"
width="106"
height="30"
fill="currentColor"
>
<title id="remix-run-logo-title">Remix Logo</title>
<path d="M0 161V136H45.5416C53.1486 136 54.8003 141.638 54.8003 145V161H0Z M133.85 124.16C135.3 142.762 135.3 151.482 135.3 161H92.2283C92.2283 158.927 92.2653 157.03 92.3028 155.107C92.4195 149.128 92.5411 142.894 91.5717 130.304C90.2905 111.872 82.3473 107.776 67.7419 107.776H54.8021H0V74.24H69.7918C88.2407 74.24 97.4651 68.632 97.4651 53.784C97.4651 40.728 88.2407 32.816 69.7918 32.816H0V0H77.4788C119.245 0 140 19.712 140 51.2C140 74.752 125.395 90.112 105.665 92.672C122.32 96 132.057 105.472 133.85 124.16Z" />
<path d="M229.43 120.576C225.59 129.536 218.422 133.376 207.158 133.376C194.614 133.376 184.374 126.72 183.35 112.64H263.478V101.12C263.478 70.1437 243.254 44.0317 205.11 44.0317C169.526 44.0317 142.902 69.8877 142.902 105.984C142.902 142.336 169.014 164.352 205.622 164.352C235.83 164.352 256.822 149.76 262.71 123.648L229.43 120.576ZM183.862 92.6717C185.398 81.9197 191.286 73.7277 204.598 73.7277C216.886 73.7277 223.542 82.4317 224.054 92.6717H183.862Z" />
<path d="M385.256 66.5597C380.392 53.2477 369.896 44.0317 349.672 44.0317C332.52 44.0317 320.232 51.7117 314.088 64.2557V47.1037H272.616V161.28H314.088V105.216C314.088 88.0638 318.952 76.7997 332.52 76.7997C345.064 76.7997 348.136 84.9917 348.136 100.608V161.28H389.608V105.216C389.608 88.0638 394.216 76.7997 408.04 76.7997C420.584 76.7997 423.4 84.9917 423.4 100.608V161.28H464.872V89.5997C464.872 65.7917 455.656 44.0317 424.168 44.0317C404.968 44.0317 391.4 53.7597 385.256 66.5597Z" />
<path d="M478.436 47.104V161.28H519.908V47.104H478.436ZM478.18 36.352H520.164V0H478.18V36.352Z" />
<path d="M654.54 47.1035H611.788L592.332 74.2395L573.388 47.1035H527.564L568.78 103.168L523.98 161.28H566.732L589.516 130.304L612.3 161.28H658.124L613.068 101.376L654.54 47.1035Z" />
</svg>
);
}

View File

@@ -1,44 +0,0 @@
import type { MetaFunction, LinksFunction } from "@remix-run/node";
import { Outlet } from "@remix-run/react";
import stylesUrl from "~/styles/demos/about.css";
export let meta: MetaFunction = () => {
return {
title: "About Remix"
};
};
export let links: LinksFunction = () => {
return [{ rel: "stylesheet", href: stylesUrl }];
};
export default function Index() {
return (
<div className="about">
<div className="about__intro">
<h2>About Us</h2>
<p>
Ok, so this page isn't really <em>about us</em>, but we did want to
show you a few more things Remix can do.
</p>
<p>
Did you notice that things look a little different on this page? The
CSS that we import in the route file and include in its{" "}
<code>links</code> export is only included on this route and its
children.
</p>
<p>
Wait a sec...<em>its children</em>? To understand what we mean by
this,{" "}
<a href="https://remix.run/docs/en/v1/guides/routing">
read all about nested routes in the docs
</a>
.
</p>
<hr />
<Outlet />
</div>
</div>
);
}

View File

@@ -1,17 +0,0 @@
import { Link } from "@remix-run/react";
export default function AboutIndex() {
return (
<div>
<p>
You are looking at the index route for the <code>/about</code> URL
segment, but there are nested routes as well!
</p>
<p>
<strong>
<Link to="whoa">Check out one of them here.</Link>
</strong>
</p>
</div>
);
}

View File

@@ -1,20 +0,0 @@
import { Link } from "@remix-run/react";
export default function AboutIndex() {
return (
<div>
<p>
Whoa, this is a nested route! We render the <code>/about</code> layout
route component, and its <code>Outlet</code> renders our route
component. 🤯
</p>
<p>
<strong>
<Link to="..">
Go back to the <code>/about</code> index.
</Link>
</strong>
</p>
</div>
);
}

View File

@@ -1,102 +0,0 @@
import type { ActionFunction } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import { useEffect, useRef } from "react";
export function meta() {
return { title: "Actions Demo" };
}
// When your form sends a POST, the action is called on the server.
// - https://remix.run/api/conventions#action
// - https://remix.run/guides/data-updates
export let action: ActionFunction = async ({ request }) => {
let formData = await request.formData();
let answer = formData.get("answer");
// Typical action workflows start with validating the form data that just came
// over the network. Clientside validation is fine, but you definitely need it
// server side. If there's a problem, return the the data and the component
// can render it.
if (typeof answer !== "string") {
return json("Come on, at least try!", { status: 400 });
}
if (answer !== "egg") {
return json(`Sorry, ${answer} is not right.`, { status: 400 });
}
// Finally, if the data is valid, you'll typically write to a database or send or
// email or log the user in, etc. It's recommended to redirect after a
// successful action, even if it's to the same place so that non-JavaScript workflows
// from the browser doesn't repost the data if the user clicks back.
return redirect("/demos/correct");
};
export default function ActionsDemo() {
// https://remix.run/api/remix#useactiondata
let actionMessage = useActionData<string>();
let answerRef = useRef<HTMLInputElement>(null);
// This form works without JavaScript, but when we have JavaScript we can make
// the experience better by selecting the input on wrong answers! Go ahead, disable
// JavaScript in your browser and see what happens.
useEffect(() => {
if (actionMessage && answerRef.current) {
answerRef.current.select();
}
}, [actionMessage]);
return (
<div className="remix__page">
<main>
<h2>Actions!</h2>
<p>
This form submission will send a post request that we handle in our
`action` export. Any route can export an action to handle data
mutations.
</p>
<Form method="post" className="remix__form">
<h3>Post an Action</h3>
<p>
<i>What is more useful when it is broken?</i>
</p>
<label>
<div>Answer:</div>
<input ref={answerRef} name="answer" type="text" />
</label>
<div>
<button>Answer!</button>
</div>
{actionMessage ? (
<p>
<b>{actionMessage}</b>
</p>
) : null}
</Form>
</main>
<aside>
<h3>Additional Resources</h3>
<ul>
<li>
Guide:{" "}
<a href="https://remix.run/guides/data-writes">Data Writes</a>
</li>
<li>
API:{" "}
<a href="https://remix.run/api/conventions#action">
Route Action Export
</a>
</li>
<li>
API:{" "}
<a href="https://remix.run/api/remix#useactiondata">
<code>useActionData</code>
</a>
</li>
</ul>
</aside>
</div>
);
}

View File

@@ -1,3 +0,0 @@
export default function NiceWork() {
return <h1>You got it right!</h1>;
}

View File

@@ -1,43 +0,0 @@
import type { MetaFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, Outlet, useCatch, useLoaderData } from "@remix-run/react";
export let meta: MetaFunction = () => ({ title: "Boundaries Demo" });
export default function Boundaries() {
return (
<div className="remix__page">
<main>
<Outlet />
</main>
<aside>
<h2>Click these Links</h2>
<ul>
<li>
<Link to=".">Start over</Link>
</li>
<li>
<Link to="one">
Param: <i>one</i>
</Link>
</li>
<li>
<Link to="two">
Param: <i>two</i>
</Link>
</li>
<li>
<Link to="this-record-does-not-exist">This will be a 404</Link>
</li>
<li>
<Link to="shh-its-a-secret">And this will be 401 Unauthorized</Link>
</li>
<li>
<Link to="kaboom">This one will throw an error</Link>
</li>
</ul>
</aside>
</div>
);
}

View File

@@ -1,111 +0,0 @@
import type { LoaderFunction, MetaFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, useCatch, useLoaderData } from "@remix-run/react";
// The `$` in route filenames becomes a pattern that's parsed from the URL and
// passed to your loaders so you can look up data.
// - https://remix.run/api/conventions#loader-params
export let loader: LoaderFunction = async ({ params }) => {
// pretend like we're using params.id to look something up in the db
if (params.id === "this-record-does-not-exist") {
// If the record doesn't exist we can't render the route normally, so
// instead we throw a 404 reponse to stop running code here and show the
// user the catch boundary.
throw new Response("Not Found", { status: 404 });
}
// now pretend like the record exists but the user just isn't authorized to
// see it.
if (params.id === "shh-its-a-secret") {
// Again, we can't render the component if the user isn't authorized. You
// can even put data in the response that might help the user rectify the
// issue! Like emailing the webmaster for access to the page. (Oh, right,
// `json` is just a Response helper that makes it easier to send JSON
// responses).
throw json({ webmasterEmail: "hello@remix.run" }, { status: 401 });
}
// Sometimes your code just blows up and you never anticipated it. Remix will
// automatically catch it and send the UI to the error boundary.
if (params.id === "kaboom") {
lol();
}
// but otherwise the record was found, user has access, so we can do whatever
// else we needed to in the loader and return the data. (This is boring, we're
// just gonna return the params.id).
return { param: params.id };
};
export default function ParamDemo() {
let data = useLoaderData();
return (
<h1>
The param is <i style={{ color: "red" }}>{data.param}</i>
</h1>
);
}
// https://remix.run/api/conventions#catchboundary
// https://remix.run/api/remix#usecatch
// https://remix.run/api/guides/not-found
export function CatchBoundary() {
let caught = useCatch();
let message: React.ReactNode;
switch (caught.status) {
case 401:
message = (
<p>
Looks like you tried to visit a page that you do not have access to.
Maybe ask the webmaster ({caught.data.webmasterEmail}) for access.
</p>
);
case 404:
message = (
<p>Looks like you tried to visit a page that does not exist.</p>
);
default:
message = (
<p>
There was a problem with your request!
<br />
{caught.status} {caught.statusText}
</p>
);
}
return (
<>
<h2>Oops!</h2>
<p>{message}</p>
<p>
(Isn't it cool that the user gets to stay in context and try a different
link in the parts of the UI that didn't blow up?)
</p>
</>
);
}
// https://remix.run/api/conventions#errorboundary
// https://remix.run/api/guides/not-found
export function ErrorBoundary({ error }: { error: Error }) {
console.error(error);
return (
<>
<h2>Error!</h2>
<p>{error.message}</p>
<p>
(Isn't it cool that the user gets to stay in context and try a different
link in the parts of the UI that didn't blow up?)
</p>
</>
);
}
export let meta: MetaFunction = ({ data }) => {
return {
title: data ? `Param: ${data.param}` : "Oops...",
};
};

View File

@@ -1,41 +0,0 @@
import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, Outlet, useCatch, useLoaderData } from "@remix-run/react";
export default function Boundaries() {
return (
<>
<h2>Params</h2>
<p>
When you name a route segment with $ like{" "}
<code>routes/users/$userId.js</code>, the $ segment will be parsed from
the URL and sent to your loaders and actions by the same name.
</p>
<h2>Errors</h2>
<p>
When a route throws and error in it's action, loader, or component,
Remix automatically catches it, won't even try to render the component,
but it will render the route's ErrorBoundary instead. If the route
doesn't have one, it will bubble up to the routes above it until it hits
the root.
</p>
<p>So be as granular as you want with your error handling.</p>
<h2>Not Found</h2>
<p>
(and other{" "}
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses">
client errors
</a>
)
</p>
<p>
Loaders and Actions can throw a <code>Response</code> instead of an
error and Remix will render the CatchBoundary instead of the component.
This is great when loading data from a database isn't found. As soon as
you know you can't render the component normally, throw a 404 response
and send your app into the catch boundary. Just like error boundaries,
catch boundaries bubble, too.
</p>
</>
);
}

View File

@@ -1,101 +1,32 @@
import type { MetaFunction, LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { Link, useLoaderData } from "@remix-run/react";
type IndexData = {
resources: Array<{ name: string; url: string }>;
demos: Array<{ name: string; to: string }>;
};
// Loaders provide data to components and are only ever called on the server, so
// you can connect to a database or run any server side code you want right next
// to the component that renders it.
// https://remix.run/api/conventions#loader
export let loader: LoaderFunction = () => {
let data: IndexData = {
resources: [
{
name: "Remix Docs",
url: "https://remix.run/docs"
},
{
name: "React Router Docs",
url: "https://reactrouter.com/docs"
},
{
name: "Remix Discord",
url: "https://discord.gg/VBePs6d"
}
],
demos: [
{
to: "demos/actions",
name: "Actions"
},
{
to: "demos/about",
name: "Nested Routes, CSS loading/unloading"
},
{
to: "demos/params",
name: "URL Params and Error Boundaries"
}
]
};
// https://remix.run/api/remix#json
return json(data);
};
// https://remix.run/api/conventions#meta
export let meta: MetaFunction = () => {
return {
title: "Remix Starter",
description: "Welcome to remix!"
};
};
// https://remix.run/guides/routing#index-routes
export default function Index() {
let data = useLoaderData<IndexData>();
return (
<div className="remix__page">
<main>
<h2>Welcome to Remix!</h2>
<p>We're stoked that you're here. 🥳</p>
<p>
Feel free to take a look around the code to see how Remix does things,
it might be a bit different than what youre used to. When you're
ready to dive deeper, we've got plenty of resources to get you
up-and-running quickly.
</p>
<p>
Check out all the demos in this starter, and then just delete the{" "}
<code>app/routes/demos</code> and <code>app/styles/demos</code>{" "}
folders when you're ready to turn this into your next project.
</p>
</main>
<aside>
<h2>Demos In This App</h2>
<ul>
{data.demos.map(demo => (
<li key={demo.to} className="remix__page__resource">
<Link to={demo.to} prefetch="intent">
{demo.name}
</Link>
</li>
))}
</ul>
<h2>Resources</h2>
<ul>
{data.resources.map(resource => (
<li key={resource.url} className="remix__page__resource">
<a href={resource.url}>{resource.name}</a>
</li>
))}
</ul>
</aside>
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
<h1>Welcome to Remix</h1>
<ul>
<li>
<a
target="_blank"
href="https://remix.run/tutorials/blog"
rel="noreferrer"
>
15m Quickstart Blog Tutorial
</a>
</li>
<li>
<a
target="_blank"
href="https://remix.run/tutorials/jokes"
rel="noreferrer"
>
Deep Dive Jokes App Tutorial
</a>
</li>
<li>
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
Remix Docs
</a>
</li>
</ul>
</div>
);
}

View File

@@ -1,7 +0,0 @@
:root {
--color-foreground: hsl(0, 0%, 100%);
--color-background: hsl(0, 0%, 7%);
--color-links: hsl(213, 100%, 73%);
--color-links-hover: hsl(213, 100%, 80%);
--color-border: hsl(0, 0%, 25%);
}

View File

@@ -1,26 +0,0 @@
/*
* Whoa whoa whoa, wait a sec...why are we overriding global CSS selectors?
* Isn't that kind of scary? How do we know this won't have side effects?
*
* In Remix, CSS that is included in a route file will *only* show up on that
* route (and for nested routes, its children). When the user navigates away
* from that route the CSS files linked from those routes will be automatically
* unloaded, making your styles much easier to predict and control.
*
* Read more about styling routes in the docs:
* https://remix.run/guides/styling
*/
:root {
--color-foreground: hsl(0, 0%, 7%);
--color-background: hsl(56, 100%, 50%);
--color-links: hsl(345, 56%, 39%);
--color-links-hover: hsl(345, 51%, 49%);
--color-border: rgb(184, 173, 20);
--font-body: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
Liberation Mono, Courier New, monospace;
}
.about__intro {
max-width: 500px;
}

View File

@@ -1,216 +0,0 @@
/*
* You can just delete everything here or keep whatever you like, it's just a
* quick baseline!
*/
:root {
--color-foreground: hsl(0, 0%, 7%);
--color-background: hsl(0, 0%, 100%);
--color-links: hsl(213, 100%, 52%);
--color-links-hover: hsl(213, 100%, 43%);
--color-border: hsl(0, 0%, 82%);
--font-body: -apple-system, "Segoe UI", Helvetica Neue, Helvetica, Roboto,
Arial, sans-serif, system-ui, "Apple Color Emoji", "Segoe UI Emoji";
}
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
:-moz-focusring {
outline: auto;
}
:focus {
outline: var(--color-links) solid 2px;
outline-offset: 2px;
}
html,
body {
padding: 0;
margin: 0;
background-color: var(--color-background);
color: var(--color-foreground);
}
body {
font-family: var(--font-body);
line-height: 1.5;
}
a {
color: var(--color-links);
text-decoration: none;
}
a:hover {
color: var(--color-links-hover);
text-decoration: underline;
}
hr {
display: block;
height: 1px;
border: 0;
background-color: var(--color-border);
margin-top: 2rem;
margin-bottom: 2rem;
}
input:where([type="text"]),
input:where([type="search"]) {
display: block;
border: 1px solid var(--color-border);
width: 100%;
font: inherit;
line-height: 1;
height: calc(1ch + 1.5em);
padding-right: 0.5em;
padding-left: 0.5em;
background-color: hsl(0 0% 100% / 20%);
color: var(--color-foreground);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.container {
--gutter: 16px;
width: 1024px;
max-width: calc(100% - var(--gutter) * 2);
margin-right: auto;
margin-left: auto;
}
.remix-app {
display: flex;
flex-direction: column;
min-height: 100vh;
min-height: calc(100vh - env(safe-area-inset-bottom));
}
.remix-app > * {
width: 100%;
}
.remix-app__header {
padding-top: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--color-border);
}
.remix-app__header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.remix-app__header-home-link {
width: 106px;
height: 30px;
color: var(--color-foreground);
}
.remix-app__header-nav ul {
list-style: none;
margin: 0;
display: flex;
align-items: center;
gap: 1.5em;
}
.remix-app__header-nav li {
font-weight: bold;
}
.remix-app__main {
flex: 1 1 100%;
}
.remix-app__footer {
padding-top: 1rem;
padding-bottom: 1rem;
border-top: 1px solid var(--color-border);
}
.remix-app__footer-content {
display: flex;
justify-content: center;
align-items: center;
}
.remix__page {
--gap: 1rem;
--space: 2rem;
display: grid;
grid-auto-rows: min-content;
gap: var(--gap);
padding-top: var(--space);
padding-bottom: var(--space);
}
@media print, screen and (min-width: 640px) {
.remix__page {
--gap: 2rem;
grid-auto-rows: unset;
grid-template-columns: repeat(2, 1fr);
}
}
@media screen and (min-width: 1024px) {
.remix__page {
--gap: 4rem;
}
}
.remix__page > main > :first-child {
margin-top: 0;
}
.remix__page > main > :last-child {
margin-bottom: 0;
}
.remix__page > aside {
margin: 0;
padding: 1.5ch 2ch;
border: solid 1px var(--color-border);
border-radius: 0.5rem;
}
.remix__page > aside > :first-child {
margin-top: 0;
}
.remix__page > aside > :last-child {
margin-bottom: 0;
}
.remix__form {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
border: 1px solid var(--color-border);
border-radius: 0.5rem;
}
.remix__form > * {
margin-top: 0;
margin-bottom: 0;
}

23926
examples/remix/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,21 +6,22 @@
"dev": "remix dev"
},
"dependencies": {
"@remix-run/node": "^1.5.1",
"@remix-run/react": "^1.5.1",
"@remix-run/vercel": "^1.5.1",
"@vercel/node": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
"@remix-run/node": "^1.7.5",
"@remix-run/react": "^1.7.5",
"@remix-run/vercel": "^1.7.5",
"@vercel/analytics": "^0.1.3",
"@vercel/node": "^2.4.4",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "^1.5.1",
"@remix-run/eslint-config": "^1.5.1",
"@remix-run/serve": "^1.5.1",
"@types/react": "^17.0.45",
"@types/react-dom": "^17.0.17",
"eslint": "^8.15.0",
"typescript": "^4.6.4"
"@remix-run/dev": "^1.7.5",
"@remix-run/eslint-config": "^1.7.5",
"@remix-run/serve": "^1.7.5",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"eslint": "^8.23.1",
"typescript": "^4.7.4"
},
"engines": {
"node": ">=14"

View File

@@ -1,6 +1,4 @@
/**
* @type {import('@remix-run/dev').AppConfig}
*/
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
serverBuildTarget: "vercel",
// When running locally in development mode, we use the built in remix

View File

@@ -1,2 +1,2 @@
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/node/globals" />
/// <reference types="@remix-run/node" />

View File

@@ -1,4 +1,4 @@
import * as build from "@remix-run/dev/server-build";
import { createRequestHandler } from "@remix-run/vercel";
import * as build from "@remix-run/dev/server-build";
export default createRequestHandler({ build, mode: process.env.NODE_ENV });

View File

@@ -2,12 +2,16 @@
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"moduleResolution": "node",
"resolveJsonModule": true,
"target": "ES2019",
"strict": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},

View File

@@ -7,12 +7,16 @@
"type": "module",
"private": true,
"devDependencies": {
"solid-app-router": "^0.3.2",
"solid-js": "^1.3.15",
"solid-meta": "^0.27.3",
"solid-start": "next",
"solid-start-vercel": "next",
"vite": "^2.9.9"
"solid-start-vercel": "^0.2.0",
"typescript": "^4.8.3",
"vite": "^3.1.0"
},
"dependencies": {
"@solidjs/meta": "^0.28.2",
"@solidjs/router": "^0.5.0",
"solid-js": "^1.6.0",
"solid-start": "^0.2.0",
"undici": "^5.11.0"
},
"engines": {
"node": "16.x"

View File

@@ -1,4 +1,3 @@
import { hydrate } from "solid-js/web";
import { StartClient } from "solid-start/entry-client";
import { mount, StartClient } from 'solid-start/entry-client';
hydrate(() => <StartClient />, document);
mount(() => <StartClient />, document);

View File

@@ -1,7 +1,9 @@
import { StartServer, createHandler, renderAsync } from "solid-start/entry-server";
import { inlineServerModules } from "solid-start/server";
import {
StartServer,
createHandler,
renderAsync,
} from 'solid-start/entry-server';
export default createHandler(
inlineServerModules,
renderAsync((context) => <StartServer context={context} />)
renderAsync(event => <StartServer event={event} />)
);

View File

@@ -1,25 +1,33 @@
// @refresh reload
import { Links, Meta, Routes, Scripts } from "solid-start/root";
import { ErrorBoundary } from "solid-start/error-boundary";
import { Suspense } from "solid-js";
import {
Html,
Head,
Body,
Meta,
Routes,
FileRoutes,
Scripts,
ErrorBoundary,
} from 'solid-start';
import { Suspense } from 'solid-js';
export default function Root() {
return (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Html lang="en">
<Head>
<Meta charset="utf-8" />
<Meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Body>
<ErrorBoundary>
<Suspense>
<Routes />
<Routes>
<FileRoutes />
</Routes>
</Suspense>
</ErrorBoundary>
<Scripts />
</body>
</html>
</Body>
</Html>
);
}

View File

@@ -8,8 +8,8 @@ export default function Home() {
<Counter />
<p>
Visit{" "}
<a href="https://solidjs.com" target="_blank">
solidjs.com
<a href="https://start.solidjs.com" target="_blank">
start.solidjs.com
</a>{" "}
to learn how to build Solid apps.
</p>

View File

@@ -1,7 +0,0 @@
{
"build": {
"env": {
"ENABLE_VC_BUILD": "1"
}
}
}

View File

@@ -1,5 +1,5 @@
import { defineConfig } from "vite";
import solid from "solid-start";
import solid from "solid-start/vite";
import vercel from "solid-start-vercel";
export default defineConfig({

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "5.5.6",
"version": "5.5.8",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -492,7 +492,7 @@ export async function runNpmInstall(
try {
await spawnAsync(cliType, commandArgs, opts);
} catch (_) {
} catch (err: unknown) {
const potentialErrorPath = path.join(
process.env.HOME || '/',
'.npm',
@@ -508,6 +508,8 @@ export async function runNpmInstall(
);
commandArgs.push('--legacy-peer-deps');
await spawnAsync(cliType, commandArgs, opts);
} else {
throw err;
}
}
debug(`Install complete [${Date.now() - installTime}ms]`);

View File

@@ -1,3 +1,5 @@
let spawnExitCode = 0;
const spawnMock = jest.fn();
jest.mock('cross-spawn', () => {
const spawn = (...args: any) => {
@@ -5,7 +7,7 @@ jest.mock('cross-spawn', () => {
const child = {
on: (type: string, fn: (code: number) => void) => {
if (type === 'close') {
return fn(0);
return fn(spawnExitCode);
}
},
};
@@ -15,6 +17,7 @@ jest.mock('cross-spawn', () => {
});
afterEach(() => {
spawnExitCode = 0;
spawnMock.mockClear();
});
@@ -195,3 +198,27 @@ it('should only invoke `runNpmInstall()` once per `package.json` file (parallel)
env: expect.any(Object),
});
});
it('should throw error when install failed - yarn', async () => {
spawnExitCode = 1;
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '19-yarn-v2');
await expect(
runNpmInstall(fixture, [], undefined, meta)
).rejects.toMatchObject({
name: 'Error',
message: 'Command "yarn install" exited with 1',
});
});
it('should throw error when install failed - npm', async () => {
spawnExitCode = 1;
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
await expect(
runNpmInstall(fixture, [], undefined, meta)
).rejects.toMatchObject({
name: 'Error',
message: 'Command "npm install" exited with 1',
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.4.13",
"version": "28.5.2",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -12,7 +12,7 @@
},
"scripts": {
"preinstall": "node ./scripts/preinstall.js",
"test": "jest --env node --verbose --runInBand --bail --forceExit",
"test": "jest --env node --verbose --bail --forceExit",
"test-unit": "yarn test test/unit/",
"test-integration-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
"test-integration-dev": "yarn test test/dev/",
@@ -41,16 +41,17 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.5.6",
"@vercel/go": "2.2.14",
"@vercel/hydrogen": "0.0.27",
"@vercel/next": "3.2.7",
"@vercel/node": "2.6.0",
"@vercel/python": "3.1.23",
"@vercel/redwood": "1.0.32",
"@vercel/remix": "1.0.33",
"@vercel/ruby": "1.3.40",
"@vercel/static-build": "1.0.33",
"@vercel/build-utils": "5.5.8",
"@vercel/go": "2.2.16",
"@vercel/hydrogen": "0.0.30",
"@vercel/next": "3.2.12",
"@vercel/node": "2.6.3",
"@vercel/python": "3.1.26",
"@vercel/redwood": "1.0.36",
"@vercel/remix": "1.0.36",
"@vercel/ruby": "1.3.42",
"@vercel/static-build": "1.0.37",
"json5": "2.2.1",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -81,7 +82,7 @@
"@types/minimatch": "3.0.3",
"@types/mri": "1.1.0",
"@types/ms": "0.7.30",
"@types/node": "11.11.0",
"@types/node": "14.18.33",
"@types/node-fetch": "2.5.10",
"@types/npm-package-arg": "6.1.0",
"@types/pluralize": "0.0.29",
@@ -95,10 +96,10 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.14",
"@vercel/error-utils": "1.0.1",
"@vercel/frameworks": "1.1.9",
"@vercel/fs-detectors": "3.4.6",
"@vercel/client": "12.2.18",
"@vercel/error-utils": "1.0.3",
"@vercel/frameworks": "1.1.12",
"@vercel/fs-detectors": "3.5.1",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",
@@ -142,6 +143,7 @@
"is-url": "1.2.2",
"jaro-winkler": "0.2.8",
"jsonlines": "0.1.1",
"line-async-iterator": "3.0.0",
"load-json-file": "3.0.0",
"mime-types": "2.1.24",
"minimatch": "3.0.4",
@@ -172,7 +174,6 @@
"typescript": "4.7.4",
"universal-analytics": "0.4.20",
"utility-types": "2.1.0",
"which": "2.0.2",
"write-json-file": "2.2.0",
"xdg-app-paths": "5.1.0",
"yauzl-promise": "2.1.3"

View File

@@ -58,6 +58,8 @@ import { sortBuilders } from '../util/build/sort-builders';
import { toEnumerableError } from '../util/error';
import { validateConfig } from '../util/validate-config';
import { setMonorepoDefaultSettings } from '../util/build/monorepo';
type BuildResult = BuildResultV2 | BuildResultV3;
interface SerializedBuilder extends Builder {
@@ -99,7 +101,7 @@ const help = () => {
${chalk.dim('Examples:')}
${chalk.gray('')} Build the project
${chalk.gray('-')} Build the project
${chalk.cyan(`$ ${cli.name} build`)}
${chalk.cyan(`$ ${cli.name} build --cwd ./path-to-project`)}
@@ -271,6 +273,7 @@ async function doBuild(
outputDir: string
): Promise<void> {
const { output } = client;
const workPath = join(cwd, project.settings.rootDirectory || '.');
const [pkg, vercelConfig, nowConfig] = await Promise.all([
@@ -301,6 +304,13 @@ async function doBuild(
...pickOverrides(localConfig),
};
if (
projectSettings.rootDirectory !== null &&
projectSettings.rootDirectory !== '.'
) {
await setMonorepoDefaultSettings(cwd, workPath, projectSettings, output);
}
// Get a list of source files
const files = (await getFiles(workPath, client)).map(f =>
normalizePath(relative(workPath, f))

View File

@@ -0,0 +1,140 @@
import fs from 'fs-extra';
import { join, relative, basename } from 'path';
import {
detectFramework,
monorepoManagers,
LocalFileSystemDetector,
packageManagers,
} from '@vercel/fs-detectors';
import { ProjectLinkAndSettings } from '../projects/project-settings';
import { Output } from '../output';
import title from 'title';
import JSON5 from 'json5';
export async function setMonorepoDefaultSettings(
cwd: string,
workPath: string,
projectSettings: ProjectLinkAndSettings['settings'],
output: Output
) {
const localFileSystem = new LocalFileSystemDetector(cwd);
const [monorepoManager, packageManager] = await Promise.all([
detectFramework({
fs: localFileSystem,
frameworkList: monorepoManagers,
}),
detectFramework({
fs: localFileSystem,
frameworkList: packageManagers,
}),
]);
const projectName = basename(workPath);
const relativeToRoot = relative(workPath, cwd);
const setCommand = (
command: 'buildCommand' | 'installCommand',
value: string
) => {
if (projectSettings[command]) {
output.warn(
`Cannot automatically assign ${command} as it is already set via project settings or configuarion overrides.`
);
} else {
projectSettings[command] = value;
}
};
if (monorepoManager) {
output.log(
`Automatically detected ${title(
monorepoManager
)} monorepo manager. Attempting to assign default \`buildCommand\` and \`installCommand\` settings.`
);
}
if (monorepoManager === 'turbo') {
// No ENOENT handling required here since conditional wouldn't be `true` unless `turbo.json` was found.
const turboJSON = JSON5.parse(
fs.readFileSync(join(cwd, 'turbo.json'), 'utf-8')
);
if (!turboJSON?.pipeline?.build) {
output.warn(
'Missing required `build` pipeline in turbo.json. Skipping automatic setting assignment.'
);
return;
}
setCommand(
'buildCommand',
`cd ${relativeToRoot} && npx turbo run build --filter=${projectName}...`
);
setCommand(
'installCommand',
`cd ${relativeToRoot} && ${packageManager} install`
);
} else if (monorepoManager === 'nx') {
// No ENOENT handling required here since conditional wouldn't be `true` unless `nx.json` was found.
const nxJSON = JSON5.parse(fs.readFileSync(join(cwd, 'nx.json'), 'utf-8'));
if (!nxJSON?.targetDefaults?.build) {
output.log(
'Missing default `build` target in nx.json. Checking for project level Nx configuration...'
);
const [projectJSONBuf, packageJsonBuf] = await Promise.all([
fs.readFile(join(workPath, 'project.json')).catch(() => null),
fs.readFile(join(workPath, 'package.json')).catch(() => null),
]);
let hasBuildTarget = false;
if (projectJSONBuf) {
output.log('Found project.json Nx configuration.');
const projectJSON = JSON5.parse(projectJSONBuf.toString('utf-8'));
if (projectJSON?.targets?.build) {
hasBuildTarget = true;
}
}
if (packageJsonBuf) {
const packageJSON = JSON5.parse(packageJsonBuf.toString('utf-8'));
if (packageJSON?.nx) {
output.log('Found package.json Nx configuration.');
if (packageJSON.nx.targets?.build) {
hasBuildTarget = true;
}
}
}
if (!hasBuildTarget) {
output.warn(
'Missing required `build` target in either project.json or package.json Nx configuration. Skipping automatic setting assignment.'
);
return;
}
}
setCommand(
'buildCommand',
`cd ${relativeToRoot} && npx nx build ${projectName}`
);
setCommand(
'installCommand',
`cd ${relativeToRoot} && ${packageManager} install`
);
}
// TODO (@Ethan-Arrowood) - Revisit rush support when we can test it better
/* else if (monorepoManager === 'rush') {
setCommand(
'buildCommand',
`node ${relativeToRoot}/common/scripts/install-run-rush.js build --to ${projectName}`
);
setCommand(
'installCommand',
`node ${relativeToRoot}/common/scripts/install-run-rush.js install`
);
} */
}

View File

@@ -1,8 +1,7 @@
import { bold } from 'chalk';
import inquirer from 'inquirer';
import { EventEmitter } from 'events';
import { URLSearchParams } from 'url';
import { parse as parseUrl } from 'url';
import { URL } from 'url';
import { VercelConfig } from '@vercel/client';
import retry, { RetryFunction, Options as RetryOptions } from 'async-retry';
import fetch, { BodyInit, Headers, RequestInit, Response } from 'node-fetch';
@@ -87,25 +86,18 @@ export default class Client extends EventEmitter implements Stdio {
}
private _fetch(_url: string, opts: FetchOptions = {}) {
const parsedUrl = parseUrl(_url, true);
const apiUrl = parsedUrl.host
? `${parsedUrl.protocol}//${parsedUrl.host}`
: '';
const url = new URL(_url, this.apiUrl);
if (opts.accountId || opts.useCurrentTeam !== false) {
const query = new URLSearchParams(parsedUrl.query);
if (opts.accountId) {
if (opts.accountId.startsWith('team_')) {
query.set('teamId', opts.accountId);
url.searchParams.set('teamId', opts.accountId);
} else {
query.delete('teamId');
url.searchParams.delete('teamId');
}
} else if (opts.useCurrentTeam !== false && this.config.currentTeam) {
query.set('teamId', this.config.currentTeam);
url.searchParams.set('teamId', this.config.currentTeam);
}
_url = `${apiUrl}${parsedUrl.pathname}?${query}`;
}
const headers = new Headers(opts.headers);
@@ -122,7 +114,6 @@ export default class Client extends EventEmitter implements Stdio {
body = opts.body;
}
const url = `${apiUrl ? '' : this.apiUrl}${_url}`;
const requestId = this.requestIdCounter++;
return this.output.time(res => {
if (res) {
@@ -130,7 +121,7 @@ export default class Client extends EventEmitter implements Stdio {
res.statusText
}: ${res.headers.get('x-vercel-id')}`;
} else {
return `#${requestId}${opts.method || 'GET'} ${url}`;
return `#${requestId}${opts.method || 'GET'} ${url.href}`;
}
}, fetch(url, { ...opts, headers, body }));
}

View File

@@ -87,8 +87,12 @@ async function createBuildProcess(
return new Promise((resolve, reject) => {
// The first message that the builder process sends is the `ready` event
buildProcess.once('message', ({ type }) => {
if (type !== 'ready') {
buildProcess.once('message', data => {
if (
data !== null &&
typeof data === 'object' &&
(data as { type: string }).type !== 'ready'
) {
reject(new Error('Did not get "ready" event from builder'));
} else {
resolve(buildProcess);

View File

@@ -31,11 +31,11 @@ export function parseListen(str: string, defaultPort = 3000): ListenSpec {
return [url.pathname];
case 'tcp:':
url.port = url.port || String(defaultPort);
return [parseInt(url.port, 10), url.hostname];
return [parseInt(url.port, 10), url.hostname ?? undefined];
default:
if (!url.slashes) {
if (url.protocol === null) {
return [defaultPort, url.pathname];
return [defaultPort, url.pathname ?? undefined];
}
port = Number(url.hostname);
if (url.protocol && !isNaN(port)) {

View File

@@ -4,7 +4,7 @@
* @param querystring - The querystring to parse, also known as the "search" string.
*/
export function parseQueryString(
querystring?: string
querystring?: string | null
): Record<string, string[]> {
const query: Record<string, string[]> = Object.create(null);
if (!querystring || !querystring.startsWith('?') || querystring === '?') {
@@ -38,9 +38,9 @@ export function parseQueryString(
*/
export function formatQueryString(
query: Record<string, string[]> | undefined
): string | undefined {
): string | null {
if (!query) {
return undefined;
return null;
}
let s = '';
let prefix = '?';
@@ -55,5 +55,5 @@ export function formatQueryString(
prefix = '&';
}
}
return s || undefined;
return s || null;
}

View File

@@ -57,7 +57,8 @@ export async function devRouter(
phase?: HandleValue | null
): Promise<RouteResult> {
let result: RouteResult | undefined;
let { pathname: reqPathname = '/', search: reqSearch } = url.parse(reqUrl);
let { pathname: reqPathname, search: reqSearch } = url.parse(reqUrl);
reqPathname ??= '/';
const reqQuery = parseQueryString(reqSearch);
const combinedHeaders: HttpHeadersConfig = { ...previousHeaders };
let status: number | undefined;
@@ -130,7 +131,8 @@ export async function devRouter(
phase !== 'hit' &&
!isDestUrl
) {
const { pathname = '/' } = url.parse(destPath);
let { pathname } = url.parse(destPath);
pathname ??= '/';
const hasDestFile = await devServer.hasFilesystem(
pathname,
vercelConfig
@@ -186,8 +188,9 @@ export async function devRouter(
if (!destPath.startsWith('/')) {
destPath = `/${destPath}`;
}
const { pathname: destPathname = '/', search: destSearch } =
let { pathname: destPathname, search: destSearch } =
url.parse(destPath);
destPathname ??= '/';
const destQuery = parseQueryString(destSearch);
Object.assign(destQuery, reqQuery);
result = {

View File

@@ -18,7 +18,6 @@ import directoryTemplate from 'serve-handler/src/directory';
import getPort from 'get-port';
import isPortReachable from 'is-port-reachable';
import deepEqual from 'fast-deep-equal';
import which from 'which';
import npa from 'npm-package-arg';
import type { ChildProcess } from 'child_process';
@@ -33,6 +32,7 @@ import {
Builder,
cloneEnv,
Env,
getNodeBinPath,
StartDevServerResult,
FileFsRef,
PackageJson,
@@ -2238,6 +2238,10 @@ export default class DevServer {
}
);
// add the node_modules/.bin directory to the PATH
const nodeBinPath = await getNodeBinPath({ cwd });
env.PATH = `${nodeBinPath}${path.delimiter}${env.PATH}`;
// This is necesary so that the dev command in the Project
// will work cross-platform (especially Windows).
let command = devCommand
@@ -2252,22 +2256,6 @@ export default class DevServer {
})}`
);
const isNpxAvailable = await which('npx')
.then(() => true)
.catch(() => false);
if (isNpxAvailable) {
command = `npx --no-install ${command}`;
} else {
const isYarnAvailable = await which('yarn')
.then(() => true)
.catch(() => false);
if (isYarnAvailable) {
command = `yarn run --silent ${command}`;
}
}
this.output.debug(`Spawning dev command: ${command}`);
const proxyPort = new RegExp(port.toString(), 'g');

View File

@@ -1,4 +1,4 @@
import Client from '../client';
import type Client from '../client';
export default async function confirm(
client: Client,

View File

@@ -34,7 +34,10 @@ export async function ensureLink(
): Promise<LinkResult | number> {
let link = await getLinkedProject(client, cwd);
if (link.status === 'not_linked') {
if (
(link.status === 'linked' && opts.forceDelete) ||
link.status === 'not_linked'
) {
link = await setupAndLink(client, cwd, opts);
if (link.status === 'not_linked') {

View File

@@ -104,13 +104,38 @@ test(
test('[vercel dev] 08-hugo', async () => {
if (process.platform === 'darwin') {
// Update PATH to find the Hugo executable installed via GH Actions
// 1. run the test without Hugo in the PATH
let tester = await testFixtureStdio(
'08-hugo',
async () => {
throw new Error('Expected dev server to fail to be ready');
},
{
readyTimeout: 2000,
// Important: for the first test, we MUST deploy this app so that the
// framework (e.g. Hugo) will be detected by the server and associated
// with the project since `vc dev` doesn't do framework detection
skipDeploy: false,
}
);
await expect(tester()).rejects.toThrow(
new Error('Dev server timed out while waiting to be ready')
);
// 2. Update PATH to find the Hugo executable installed via GH Actions
process.env.PATH = `${resolve(fixture('08-hugo'))}${delimiter}${
process.env.PATH
}`;
const tester = testFixtureStdio('08-hugo', async (testPath: any) => {
await testPath(200, '/', /Hugo/m);
});
// 3. Rerun the test now that Hugo is in the PATH
tester = testFixtureStdio(
'08-hugo',
async (testPath: any) => {
await testPath(200, '/', /Hugo/m);
},
{ skipDeploy: true }
);
await tester();
} else {
console.log(`Skipping 08-hugo on platform ${process.platform}`);

View File

@@ -61,8 +61,13 @@ function fetchWithRetry(url, opts = {}) {
function createResolver() {
let resolver;
const p = new Promise(res => (resolver = res));
let rejector;
const p = new Promise((resolve, reject) => {
resolver = resolve;
rejector = reject;
});
p.resolve = resolver;
p.reject = rejector;
return p;
}
@@ -274,7 +279,13 @@ async function testFixture(directory, opts = {}, args = []) {
function testFixtureStdio(
directory,
fn,
{ expectedCode = 0, skipDeploy, isExample, projectSettings } = {}
{
expectedCode = 0,
skipDeploy,
isExample,
projectSettings,
readyTimeout = 0,
} = {}
) {
return async () => {
const nodeMajor = Number(process.versions.node.split('.')[0]);
@@ -385,6 +396,18 @@ function testFixtureStdio(
const readyResolver = createResolver();
const exitResolver = createResolver();
// By default, tests will wait 6 minutes for the dev server to be ready and
// perform the tests, however a `readyTimeout` can be used to reduce the
// wait time if the dev server is expected to fail to start or hang
let readyTimer = null;
if (readyTimeout > 0) {
readyTimer = setTimeout(() => {
readyResolver.reject(
new Error('Dev server timed out while waiting to be ready')
);
}, readyTimeout);
}
try {
let printedOutput = false;
@@ -424,6 +447,7 @@ function testFixtureStdio(
stderr += data;
if (stripAnsi(data).includes('Ready! Available at')) {
clearTimeout(readyTimer);
readyResolver.resolve();
}
@@ -507,5 +531,6 @@ module.exports = {
shouldSkip,
fixture,
fetch,
fetchWithRetry,
validateResponseHeaders,
};

View File

@@ -1,2 +1,2 @@
!/*/.vercel
/*/.vercel/output
!/**/*/.vercel
/**/*/.vercel/output

View File

@@ -0,0 +1,5 @@
{
"orgId": ".",
"projectId": ".",
"settings": { "outputDirectory": "dist", "rootDirectory": "packages/app-1" }
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const public = path.join(__dirname, 'public');
fs.rmSync(public, { recursive: true, force: true });
fs.mkdirSync(public);
fs.writeFileSync(path.join(public, 'index.txt'), `Hello, world`);

View File

@@ -0,0 +1,4 @@
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default" } }
}

View File

@@ -0,0 +1,9 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"nx": "15.0.1"
}
}

View File

@@ -0,0 +1,8 @@
const path = require('node:path');
const fs = require('node:fs');
const world = require('app-2');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.txt'), `Hello, ${world}`);

View File

@@ -0,0 +1,23 @@
{
"name": "app-1",
"version": "0.0.1",
"scripts": {
"build": "node index.js"
},
"dependencies": {
"app-2": "*"
},
"nx": {
"targets": {
"build": {
"executor": "nx:run-script",
"options": {
"script": "build"
},
"dependsOn": [
"^build"
]
}
}
}
}

View File

@@ -0,0 +1,24 @@
{
"name": "app-2",
"version": "0.0.1",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"build": "node script.js"
},
"nx": {
"targets": {
"build": {
"executor": "nx:run-script",
"options": {
"script": "build"
},
"dependsOn": [
"^build"
]
}
}
}
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.js'), 'module.exports = "world"');

View File

@@ -0,0 +1,5 @@
{
"orgId": ".",
"projectId": ".",
"settings": { "outputDirectory": "dist", "rootDirectory": "packages/app-1" }
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const public = path.join(__dirname, 'public');
fs.rmSync(public, { recursive: true, force: true });
fs.mkdirSync(public);
fs.writeFileSync(path.join(public, 'index.txt'), `Hello, world`);

View File

@@ -0,0 +1,4 @@
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default" } }
}

View File

@@ -0,0 +1,9 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"nx": "15.0.1"
}
}

View File

@@ -0,0 +1,8 @@
const path = require('node:path');
const fs = require('node:fs');
const world = require('app-2');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.txt'), `Hello, ${world}`);

View File

@@ -0,0 +1,13 @@
{
"name": "app-1",
"version": "0.0.1",
"scripts": {
"build": "node index.js"
},
"dependencies": {
"app-2": "*"
},
"nx": {
"targets": {}
}
}

View File

@@ -0,0 +1,9 @@
{
"targets": {
"build": {
"executor": "nx:run-script",
"options": { "script": "build" },
"dependsOn": ["^build"]
}
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "app-2",
"version": "0.0.1",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"build": "node script.js"
}
}

View File

@@ -0,0 +1,9 @@
{
"targets": {
"build": {
"executor": "nx:run-script",
"options": { "script": "build" },
"dependsOn": ["^build"]
}
}
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.js'), 'module.exports = "world"');

View File

@@ -0,0 +1,5 @@
{
"orgId": ".",
"projectId": ".",
"settings": { "outputDirectory": "dist", "rootDirectory": "packages/app-1" }
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const public = path.join(__dirname, 'public');
fs.rmSync(public, { recursive: true, force: true });
fs.mkdirSync(public);
fs.writeFileSync(path.join(public, 'index.txt'), `Hello, world`);

View File

@@ -0,0 +1,4 @@
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default" } }
}

View File

@@ -0,0 +1,9 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"nx": "15.0.1"
}
}

View File

@@ -0,0 +1,8 @@
const path = require('node:path');
const fs = require('node:fs');
const world = require('app-2');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.txt'), `Hello, ${world}`);

View File

@@ -0,0 +1,23 @@
{
"name": "app-1",
"version": "0.0.1",
"scripts": {
"build": "node index.js"
},
"dependencies": {
"app-2": "*"
},
"nx": {
"targets": {
"build": {
"executor": "nx:run-script",
"options": {
"script": "build"
},
"dependsOn": [
"^build"
]
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "app-2",
"version": "0.0.1",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"build": "node script.js"
}
}

View File

@@ -0,0 +1,9 @@
{
"targets": {
"build": {
"executor": "nx:run-script",
"options": { "script": "build" },
"dependsOn": ["^build"]
}
}
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.js'), 'module.exports = "world"');

View File

@@ -0,0 +1,5 @@
{
"orgId": ".",
"projectId": ".",
"settings": { "outputDirectory": "dist", "rootDirectory": "packages/app-1" }
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const public = path.join(__dirname, 'public');
fs.rmSync(public, { recursive: true, force: true });
fs.mkdirSync(public);
fs.writeFileSync(path.join(public, 'index.txt'), `Hello, world`);

View File

@@ -0,0 +1,4 @@
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default" } }
}

View File

@@ -0,0 +1,9 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"nx": "15.0.1"
}
}

View File

@@ -0,0 +1,8 @@
const path = require('node:path');
const fs = require('node:fs');
const world = require('app-2');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.txt'), `Hello, ${world}`);

View File

@@ -0,0 +1,10 @@
{
"name": "app-1",
"version": "0.0.1",
"scripts": {
"build": "node index.js"
},
"dependencies": {
"app-2": "*"
}
}

View File

@@ -0,0 +1,9 @@
{
"targets": {
"build": {
"executor": "nx:run-script",
"options": { "script": "build" },
"dependsOn": ["^build"]
}
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "app-2",
"version": "0.0.1",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"build": "node script.js"
}
}

View File

@@ -0,0 +1,9 @@
{
"targets": {
"build": {
"executor": "nx:run-script",
"options": { "script": "build" },
"dependsOn": ["^build"]
}
}
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.js'), 'module.exports = "world"');

View File

@@ -0,0 +1,5 @@
{
"orgId": ".",
"projectId": ".",
"settings": { "outputDirectory": "dist", "rootDirectory": "packages/app-1" }
}

View File

@@ -0,0 +1,7 @@
const path = require('node:path');
const fs = require('node:fs');
const public = path.join(__dirname, 'public');
fs.rmSync(public, { recursive: true, force: true });
fs.mkdirSync(public);
fs.writeFileSync(path.join(public, 'index.txt'), `Hello, world`);

View File

@@ -0,0 +1,5 @@
{
"extends": "nx/presets/npm.json",
"tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default" } },
"targetDefaults": { "build": { "dependsOn": ["^build"] } }
}

View File

@@ -0,0 +1,9 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"devDependencies": {
"nx": "15.0.1"
}
}

View File

@@ -0,0 +1,8 @@
const path = require('node:path');
const fs = require('node:fs');
const world = require('app-2');
const dist = path.join(__dirname, 'dist');
fs.rmSync(dist, { recursive: true, force: true });
fs.mkdirSync(dist);
fs.writeFileSync(path.join(dist, 'index.txt'), `Hello, ${world}`);

View File

@@ -0,0 +1,10 @@
{
"name": "app-1",
"version": "0.0.1",
"scripts": {
"build": "node index.js"
},
"dependencies": {
"app-2": "*"
}
}

Some files were not shown because too many files have changed in this diff Show More