diff --git a/website/docs/__partials__/node/package-workspaces.mdx b/website/docs/__partials__/node/package-workspaces.mdx
new file mode 100644
index 00000000000..1c889221601
--- /dev/null
+++ b/website/docs/__partials__/node/package-workspaces.mdx
@@ -0,0 +1,66 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+
+
+```json title="package.json"
+{
+ // ...
+ "workspaces": ["apps/*", "packages/*"]
+}
+```
+
+```yaml title=".yarnrc.yml"
+# ...
+nodeLinker: 'node-modules'
+```
+
+- [Documentation](https://yarnpkg.com/features/workspaces)
+
+
+
+
+```json title="package.json"
+{
+ // ...
+ "workspaces": ["apps/*", "packages/*"]
+}
+```
+
+- [Documentation](https://classic.yarnpkg.com/en/docs/workspaces)
+
+
+
+
+```json title="package.json"
+{
+ // ...
+ "workspaces": ["apps/*", "packages/*"]
+}
+```
+
+- [Documentation](https://docs.npmjs.com/cli/v8/using-npm/workspaces)
+
+
+
+
+```yaml title="pnpm-workspace.yaml"
+packages:
+ - 'apps/*'
+ - 'packages/*'
+```
+
+- [Documentation](https://pnpm.io/workspaces)
+
+
+
diff --git a/website/docs/guides/examples/sveltekit.mdx b/website/docs/guides/examples/sveltekit.mdx
index c4cac7b29d7..fc3e7199385 100644
--- a/website/docs/guides/examples/sveltekit.mdx
+++ b/website/docs/guides/examples/sveltekit.mdx
@@ -5,6 +5,9 @@ sidebar_label: SvelteKit
import AddDepsTabs from '@site/src/components/AddDepsTabs';
import CreateDepTabs from '@site/src/components/CreateDepTabs';
+import HeadingApiLink from '@site/src/components/Docs/HeadingApiLink';
+
+
[SvelteKit](https://kit.svelte.dev) is built on [Svelte](https://svelte.dev), a UI framework that
uses a compiler to let you write breathtakingly concise components that do minimal work in the
@@ -43,14 +46,8 @@ tasks:
command: 'vite dev'
local: true
- lint:
- command: 'eslint .'
-
- # Production build (SSR)
build:
command: 'vite build'
- deps:
- - '~:lint'
inputs:
- '@group(svelte)'
outputs:
@@ -64,11 +61,8 @@ tasks:
test:
command: 'vitest'
-
- playwright:
- command: 'playwright test'
inputs:
- - '@globs(tests)'
+ - '@group(svelte)'
```
To run the SvelteKit dev server:
@@ -80,12 +74,31 @@ moon run :dev
### ESLint integration
SvelteKit provides an option to setup ESLint along with your project, with moon you can use a
-[global `lint` task](./eslint).
+[global `lint` task](./eslint). We encourage using the global `lint` task for consistency across all
+projects within the repository. With this approach, the `eslint` command itself will be ran and the
+`svelte3` rules will still be used.
-We encourage using the global `lint` task for consistency across all projects within the repository.
-With this approach, the `eslint` command itself will be ran and the `svelte3` rules will still be
-used. Additionally, we suggest disabling the linter during the build process, but is not a
-requirement. As a potential alternative, add the `lint` task as a dependency for the `build` task.
+```yaml title="/moon.yml"
+tasks:
+ # Extends the top-level lint
+ lint:
+ args:
+ - '--ext'
+ - '.ts,.svelte'
+```
+
+Be sure to enable the Svelte parser and plugin in a project local ESLint configuration file.
+
+```js title=".eslintrc.cjs"
+module.exports = {
+ plugins: ['svelte3'],
+ ignorePatterns: ['*.cjs'],
+ settings: {
+ 'svelte3/typescript': () => require('typescript'),
+ },
+ overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
+};
+```
### TypeScript integration
@@ -96,11 +109,26 @@ continuing.
At this point we'll assume that a `tsconfig.json` has been created in the application, and
typechecking works. From here we suggest utilizing a [global `typecheck` task](./typescript) for
-consistency across all projects within the repository.
+consistency across all projects within the repository. However, because Svelte isn't standard
+JavaScript, it requires the use of the `svelte-check` command for type-checking.
-Additionally, we suggest disabling the typechecker during the build process, but is not a
-requirement. As a potential alternative, add the `typecheck` task as a dependency for the `build`
-task.
+```yaml title="/moon.yml"
+workspace:
+ inheritedTasks:
+ exclude: ['typecheck']
+
+tasks:
+ typecheck-sync:
+ command: 'svelte-kit sync'
+
+ typecheck:
+ command: 'svelte-check --tsconfig ./tsconfig.json'
+ deps:
+ - 'typecheck-sync'
+ inputs:
+ - '@group(svelte)'
+ - 'tsconfig.json'
+```
In case Svelte doesn't automatically create a `tsconfig.json`, you can use the following:
diff --git a/website/docs/guides/javascript/__partials__/workspace-commands.mdx b/website/docs/guides/javascript/__partials__/workspace-commands.mdx
new file mode 100644
index 00000000000..be2823f7d9b
--- /dev/null
+++ b/website/docs/guides/javascript/__partials__/workspace-commands.mdx
@@ -0,0 +1,154 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+
+
+Install dependencies:
+
+```shell
+npm install
+```
+
+Add a package:
+
+```shell
+# At the root
+npm install
+
+# In a project
+npm install --workspace
+```
+
+Remove a package:
+
+```shell
+# At the root
+npm install
+
+# In a project
+npm install --workspace
+```
+
+Update packages:
+
+```shell
+npx npm-check-updates --interactive
+```
+
+
+
+
+Install dependencies:
+
+```shell
+pnpm install
+```
+
+Add a package:
+
+```shell
+# At the root
+pnpm add
+
+# In a project
+pnpm add --filter
+```
+
+Remove a package:
+
+```shell
+# At the root
+pnpm remove
+
+# In a project
+pnpm remove --filter
+```
+
+Update packages:
+
+```shell
+pnpm update -i -r --latest
+```
+
+
+
+
+Install dependencies:
+
+```shell
+yarn install
+```
+
+Add a package:
+
+```shell
+# At the root
+yarn add
+
+# In a project
+yarn workspace add
+```
+
+Remove a package:
+
+```shell
+# At the root
+yarn remove
+
+# In a project
+yarn workspace remove
+```
+
+Update packages:
+
+```shell
+yarn upgrade-interactive
+```
+
+
+
+
+Install dependencies:
+
+```shell
+yarn install
+```
+
+Add a package:
+
+```shell
+# At the root
+yarn add -w
+
+# In a project
+yarn workspace add
+```
+
+Remove a package:
+
+```shell
+# At the root
+yarn remove -w
+
+# In a project
+yarn workspace remove
+```
+
+Update packages:
+
+```shell
+yarn upgrade-interactive --latest
+```
+
+
+
diff --git a/website/docs/guides/javascript/node-handbook.mdx b/website/docs/guides/javascript/node-handbook.mdx
new file mode 100644
index 00000000000..2102c3d349e
--- /dev/null
+++ b/website/docs/guides/javascript/node-handbook.mdx
@@ -0,0 +1,390 @@
+---
+title: Node.js handbook
+toc_max_heading_level: 6
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Utilizing JavaScript in a monorepo can be a daunting task, especially when using Node.js, as there
+are many ways to structure your code and to configure your tools. With this handbook, we'll help
+guide you through this process.
+
+:::info
+
+This guide is a living document and will continue to be updated over time!
+
+:::
+
+## moon setup
+
+For this part of the handbook, we'll be focusing on [moon](/moon), our build system. To start,
+languages in moon act like plugins, where their functionality and support _is not_ enabled unless
+explicitly configured. We follow this approach to avoid unnecessary overhead.
+
+### Enabling the language
+
+To enable JavaScript support via Node.js, define the [`node`](../../config/toolchain#node) setting
+in [`.moon/toolchain.yml`](../../config/toolchain), even if an empty object.
+
+```yaml title=".moon/toolchain.yml"
+# Enable Node.js
+node: {}
+
+# Enable Node.js and override default settings
+node:
+ packageManager: 'pnpm'
+```
+
+This will enable the Node.js platform and provide the following automations around its ecosystem:
+
+- Node modules will automatically be installed if dependencies in `package.json` have changed, or
+ the lockfile has changed, since the last time a task has ran.
+ - We'll also take `package.json` workspaces into account and install modules in the correct
+ location; either the workspace root, in a project, or both.
+- Relationships between projects will automatically be discovered based on `dependencies`,
+ `devDependencies`, and `peerDependencies` in `package.json`.
+ - The versions of these packages will also be automatically synced when changed.
+- Tasks can be [automatically inferred](../../config/toolchain#infertasksfromscripts) from
+ `package.json` scripts.
+- And much more!
+
+### Utilizing the toolchain
+
+When a language is enabled, moon by default will assume that the language's binary is available
+within the current environment (typically on `PATH`). This has the downside of requiring all
+developers and machines to manually install the correct version of the language, _and to stay in
+sync_.
+
+Instead, you can utilize [moon's toolchain](../../concepts/toolchain), which will download and
+install the language in the background, and ensure every task is executed using the exact version
+across all machines.
+
+Enabling the toolchain is as simple as defining the [`node.version`](../../config/toolchain#version)
+setting.
+
+```yaml title=".moon/toolchain.yml"
+# Enable Node.js toolchain with an explicit version
+node:
+ version: '18.0.0'
+```
+
+### Using `package.json` names and scripts
+
+If you're looking to prototype moon, or reduce the migration effort to moon tasks, you can configure
+moon to inherit `package.json` scripts, and internally convert them to moon tasks. This can be
+achieved with the [`node.inferTasksFromScripts`](../../config/toolchain#infertasksfromscripts)
+setting.
+
+```yaml title=".moon/toolchain.yml"
+node:
+ inferTasksFromScripts: true
+```
+
+Furthermore, if you'd like to reference projects by their package name (both in configuration and on
+the command line), instead of the moon project name, enable the
+[`node.aliasPackageNames`](../../config/toolchain#aliaspackagenames) setting.
+
+```yaml title=".moon/toolchain.yml"
+node:
+ aliasPackageNames: 'name-and-scope'
+```
+
+> These are not enabled by default as we want to encourage the
+> [use of moon tasks](../../faq#can-we-use-packagejson-scripts).
+
+## Repository structure
+
+JavaScript monorepo's work best when projects are split into applications and packages, with each
+project containing its own `package.json` and dependencies. A root `package.json` must also exist
+that pieces all projects together through workspaces.
+
+For small repositories, the following structure typically works well:
+
+```
+/
+├── .moon/
+├── package.json
+├── apps/
+│ └── client/
+| ├── ...
+│ └── package.json
+│ └── server/
+| ├── ...
+│ └── package.json
+└── packages/
+ ├── components/
+ | ├── ...
+ │ └── package.json
+ ├── theme/
+ | ├── ...
+ │ └── package.json
+ └── utils/
+ ├── ...
+ └── package.json
+```
+
+For large repositories, grouping projects by team or department helps with ownership and
+organization. With this structure, applications and libraries can be nested at any depth.
+
+```
+/
+├── .moon/
+├── package.json
+├── infra/
+│ └── ...
+├── internal/
+│ └── ...
+├── payments/
+│ └── ...
+└── shared/
+ └── ...
+```
+
+### Applications
+
+Applications are runnable or executable, like an HTTP server, and are pieced together with packages
+and its own encapsulated code. They represent the whole, while packages are the pieces. Applications
+can import and depend on packages, but they _must not_ import and depend on other applications.
+
+In moon, you can denote a project as an application using the [`type`](../../config/project#type)
+setting in [`moon.yml`](../../config/project).
+
+```yaml title="moon.yml"
+type: 'application'
+```
+
+### Packages
+
+Packages (also known as a libraries) are self-contained reusable pieces of code, and are the
+suggested pattern for [code sharing](#code-sharing). Packages can import and depend on other
+packages, but they _must not_ import and depend on applications!
+
+In moon, you can denote a project as a library using the [`type`](../../config/project#type) setting
+in [`moon.yml`](../../config/project).
+
+```yaml title="moon.yml"
+type: 'library'
+```
+
+### Configuration
+
+Every tool that you'll utilize in a repository will have its own configuration file. This will be a
+lot of config files, but regardless of what tool it is, where the config file should go will fall
+into 1 of these categories:
+
+- **Settings are inherited by all projects.** These are known as universal tools, and enforce code
+ consistency and quality across the entire repository. Their config file must exist in the
+ repository root, but may support overrides in each project.
+ - Examples: Babel, [ESLint](../examples/eslint), [Prettier](../examples/prettier),
+ [TypeScript](../examples/typescript)
+- **Settings are unique per project.** These are developers tools that must be configured separately
+ for each project, as they'll have different concerns. Their config file must exist in each
+ project, but a shared configuration may exist as a base (for example, Jest presets).
+ - Examples: [Jest](../examples/jest), [TypeScript](../examples/typescript) (with project
+ references)
+- **Settings are one-offs.** These are typically for applications or tools that require their own
+ config, but aren't prevalent throughout the entire repository.
+ - Examples: [Astro](../examples/astro), [Next](../examples/next), [Nuxt](../examples/nuxt),
+ [Remix](../examples/remix), Tailwind
+
+## Dependency management
+
+Dependencies, also known as node modules, are required by all projects, and are installed through a
+package manager like npm, pnpm, or yarn. It doesn't matter which package manager you choose, but we
+highly suggest choosing one that has proper workspaces support. If you're unfamiliar with
+workspaces, they will:
+
+- Resolve all `package.json`'s in a repository using glob patterns.
+- Install dependencies from all `package.json`'s at once, in the required locations.
+- Create symlinks of local packages in `node_modules` (to emulate an installed package).
+- Deduplicate and hoist `node_modules` when applicable.
+
+All of this functionality enables robust monorepo support, and can be enabled with the following:
+
+import PackageWorkspaces from '../../__partials__/node/package-workspaces.mdx';
+
+
+
+:::caution
+
+Package workspaces are not a requirement for monorepos, but they do solve an array of problems
+around module resolution, avoiding duplicate packages in bundles, and general interoperability.
+Proceed with caution for non-workspaces setups!
+
+:::
+
+### Workspace commands
+
+The following common commands can be used for adding, removing, or managing dependencies in a
+workspace. View the package manager's official documentation for a thorough list of commands.
+
+import WsCommands from './__partials__/workspace-commands.mdx';
+
+
+
+### Developer tools at the root
+
+While not a strict guideline to follow, we've found that installing universal developer tool related
+dependencies (Babel, ESLint, Jest, TypeScript, etc) in the root `package.json` as `devDependencies`
+to be a good pattern for consistency, quality, and the health of the repository. It provides the
+following benefits:
+
+- It ensures all projects are utilizing the same version (and sometimes configuration) of a tool.
+- It allows the tool to easily be upgraded. Upgrade once, applied everywhere.
+- It avoids conflicting or outdated versions of the same package.
+
+With that being said, this _does not_ include development dependencies that are unique to a project!
+
+### Product libraries in a project
+
+Product, application, and or framework specific packages should be installed as production
+`dependencies` in a project's `package.json`. We've found this pattern to work well for the
+following reasons:
+
+- Application dependencies are pinned per project, avoiding accidental regressions.
+- Applications can upgrade their dependencies and avoid breaking neighbor applications.
+
+## Code sharing
+
+One of the primary reasons to use a monorepo is to easily share code between projects. When code is
+co-located within the same repository, it avoids the overhead of the "build -> version -> publish to
+registry -> upgrade in consumer" workflow (when the code is located in an external repository).
+
+Co-locating code also provides the benefit of fast iteration, fast adoption, and easier migration
+(when making breaking changes for example).
+
+With [package workspaces](#dependency-management), code sharing is a breeze. As mentioned above,
+every project that contains a `package.json` that is part of the workspace, will be symlinked into
+`node_modules`. Because of this, these packages can easily be imported using their `package.json`
+name.
+
+```ts
+// Imports from /packages/utils/package.json
+import utils from '@company/utils';
+```
+
+### Depending on packages
+
+Because packages are symlinked into `node_modules`, we can depend on them as if they were normal npm
+packages, but with 1 key difference. Since these packages aren't published, they do not have a
+version to reference, and instead, we can use the special `workspace:^` version (yarn and pnpm only,
+use `*` for npm).
+
+```json
+{
+ "name": "@company/consumer",
+ "dependencies": {
+ "@company/provider": "workspace:^"
+ }
+}
+```
+
+The `workspace:` version basically means "use the package found in the current workspace". The `:^`
+determines the version range to _substitute with when publishing_. For example, the `workspace:^`
+above would be replaced with version of `@company/provider` as `^` when the
+`@company/consumer` package is published.
+
+There's also `workspace:~` and `workspace:*` which substitutes to `~` and ``
+respectively. We suggest using `:^` so that version ranges can be deduped.
+
+### Types of packages
+
+When sharing packages in a monorepo, there's typically 3 different kinds of packages:
+
+#### Local only
+
+A local only package is just that, it's only available locally to the repository and _is not_
+published to a registry, and _is not_ available to external repositories. For teams and companies
+that utilize a single repository, this will be the most common type of package.
+
+A benefit of local packages is that they do not require a build step, as source files can be
+imported directly ([when configured correctly](#bundler-integration)). This avoids a lot of
+`package.json` overhead, especially in regards to `exports`, `imports`, and other import patterns.
+
+#### Internally published
+
+An internal package is published to a private registry, and _is not_ available to the public.
+Published packages are far more strict than local packages, as the `package.json` structure plays a
+much larger role for downstream consumers, as it dictates how files are imported, where they can be
+found, what type of formats are supported (CJS, ESM), so on and so forth.
+
+Published packages require a build step, for both source code and TypeScript types (when
+applicable). We suggest using [esbuild](https://esbuild.github.io/) or
+[Packemon](../examples/packemon) to handle this entire flow. With that being said, local projects
+can still [import their source files](#bundler-integration).
+
+#### Externally published
+
+An external package is structured similarly to an internal package, but instead of publishing to a
+private registry, it's published to the npm public registry.
+
+External packages are primarily for open source projects, and require the repository to also be
+public.
+
+### Bundler integration
+
+Co-locating packages is great, but how do you import and use them effectively? The easiest solution
+is to configure resolver aliases within your bundler (Webpack, Vite, etc). By doing so, you enable
+the following functionality:
+
+- Avoids having to build (and rebuild) the package everytime its code changes.
+- Enables file system watching of the package, not just the application.
+- Allows for hot module reloading (HMR) to work.
+- Package code is transpiled and bundled alongside application code.
+
+
+
+
+```ts title="vite.config.ts"
+import path from 'path';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ // ...
+ resolve: {
+ alias: {
+ '@company/utils': path.join(__dirname, '../packages/utils/src'),
+ },
+ },
+});
+```
+
+
+
+
+```ts title="webpack.config.js"
+const path = require('path');
+
+module.exports = {
+ // ...
+ resolve: {
+ alias: {
+ '@company/utils': path.join(__dirname, '../packages/utils/src'),
+ },
+ },
+};
+```
+
+
+
+
+:::info
+
+When configuring aliases, we suggest using the `package.json` name as the alias! This ensures that
+on the consuming side, you're using the package as if it's a normal node module, and avoids
+deviating from the ecosystem.
+
+:::
+
+### TypeScript integration
+
+We suggest using TypeScript project references. Luckily, we have an
+[in-depth guide on how to properly and efficiently integrate them](./typescript-project-refs)!
diff --git a/website/docs/setup-toolchain.mdx b/website/docs/setup-toolchain.mdx
index 343ac4bc2e9..601ca5e6a0c 100644
--- a/website/docs/setup-toolchain.mdx
+++ b/website/docs/setup-toolchain.mdx
@@ -37,69 +37,9 @@ moon was built with monorepos in mind, and as such, has first-class support for
workspaces (but is not a requirement). To utilize workspaces, enable them for your chosen package
manager.
-
-
-
-```json title="package.json"
-{
- // ...
- "workspaces": ["apps/*", "packages/*"]
-}
-```
-
-```yaml title=".yarnrc.yml"
-# ...
-nodeLinker: 'node-modules'
-```
-
-- [Documentation](https://yarnpkg.com/features/workspaces)
-
-
-
-
-```json title="package.json"
-{
- // ...
- "workspaces": ["apps/*", "packages/*"]
-}
-```
-
-- [Documentation](https://classic.yarnpkg.com/en/docs/workspaces)
-
-
-
-
-```json title="package.json"
-{
- // ...
- "workspaces": ["apps/*", "packages/*"]
-}
-```
-
-- [Documentation](https://docs.npmjs.com/cli/v8/using-npm/workspaces)
-
-
-
-
-```yaml title="pnpm-workspace.yaml"
-packages:
- - 'apps/*'
- - 'packages/*'
-```
-
-- [Documentation](https://pnpm.io/workspaces)
+import PackageWorkspaces from './__partials__/node/package-workspaces.mdx';
-
-
+
## Configuring Node.js
diff --git a/website/sidebars.js b/website/sidebars.js
index 4e54377a3bf..f41514f6a21 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -169,6 +169,7 @@ const sidebars = {
label: 'JavaScript',
collapsed: false,
items: [
+ 'guides/javascript/node-handbook',
'guides/javascript/typescript-project-refs',
{
type: 'category',