@alexaegis/turbowatch 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs ADDED
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const common = require("@alexaegis/common");
4
+ const workspaceTools = require("@alexaegis/workspace-tools");
5
+ const node_child_process = require("node:child_process");
6
+ const node_path = require("node:path");
7
+ const fs = require("@alexaegis/fs");
8
+ const normalizeTurbowatchLocalNodeModulesOptions = (options) => {
9
+ return {
10
+ ...fs.normalizeCwdOption(options),
11
+ deep: options?.deep ?? true,
12
+ useGitIgnore: options?.useGitIgnore ?? false,
13
+ logChangedFiles: options?.logChangedFiles ?? false,
14
+ buildDependenciesScript: options?.buildDependenciesScript ?? "build:dependencies",
15
+ packageManagerCommand: options?.packageManagerCommand ?? "pnpm",
16
+ onFirstBuild: options?.onFirstBuild,
17
+ devScript: options?.devScript ?? "dev_"
18
+ };
19
+ };
20
+ const turbowatchLocalNodeModules = async (rawOptions) => {
21
+ const options = normalizeTurbowatchLocalNodeModulesOptions(rawOptions);
22
+ console.log("turbowatch started in", options.cwd);
23
+ const currentPackagePath = workspaceTools.getCurrentPackageRoot(options.cwd);
24
+ if (!currentPackagePath) {
25
+ throw new Error("Not in a package!");
26
+ }
27
+ const currentPackagesNodeModulesPath = node_path.join(currentPackagePath, workspaceTools.NODE_MODULES_DIRECTORY_NAME);
28
+ const workspacePackages = await workspaceTools.collectWorkspacePackages({
29
+ ...options,
30
+ skipWorkspaceRoot: true
31
+ });
32
+ const doNotMatchPackageJson = ["not", ["match", "package.json", "basename"]];
33
+ const matchInLocalPackageDirectories = [
34
+ "anyof",
35
+ ...workspacePackages.map((workspacePackage) => workspacePackage.packageJson.name).filter(common.isNotNullish).map((packageName) => ["dirname", packageName])
36
+ ];
37
+ const commonIgnoredDirs = [
38
+ ["dirname", "dist"],
39
+ ["dirname", ".turbo"],
40
+ ["dirname", ".vercel"],
41
+ ["dirname", ".cache"],
42
+ ["dirname", "coverage"],
43
+ ["dirname", "build"],
44
+ ["match", "vite(st)?.config.*"]
45
+ ];
46
+ if (!options.deep) {
47
+ commonIgnoredDirs.push(["dirname", "node_modules"]);
48
+ }
49
+ const doNotMatchCommonOutputs = ["not", ["anyof", ...commonIgnoredDirs]];
50
+ const watchExpression = [
51
+ "allof",
52
+ matchInLocalPackageDirectories,
53
+ doNotMatchCommonOutputs,
54
+ doNotMatchPackageJson
55
+ ];
56
+ if (options.useGitIgnore) {
57
+ let ignoreEntries = await workspaceTools.collectIgnoreEntries(options);
58
+ if (options.deep) {
59
+ ignoreEntries = ignoreEntries.filter(
60
+ (entry) => !entry.includes(workspaceTools.NODE_MODULES_DIRECTORY_NAME)
61
+ );
62
+ }
63
+ const ignoreMatchEntries = ignoreEntries.map((ignoreEntry) => [
64
+ "match",
65
+ ignoreEntry
66
+ ]);
67
+ const doNotMatchIgnored = ["not", ["anyof", ...ignoreMatchEntries]];
68
+ watchExpression.push(doNotMatchIgnored);
69
+ }
70
+ let changeCount = 0;
71
+ const startCommand = () => {
72
+ return node_child_process.spawn(options.packageManagerCommand, ["run", options.devScript], {
73
+ stdio: "inherit"
74
+ });
75
+ };
76
+ let spawnedOnFirstBuild;
77
+ return {
78
+ project: currentPackagesNodeModulesPath,
79
+ debounce: { wait: 50 },
80
+ triggers: [
81
+ {
82
+ expression: watchExpression,
83
+ name: "build",
84
+ retry: { retries: 0 },
85
+ onChange: async ({ spawn: spawn2, files }) => {
86
+ if (options.logChangedFiles) {
87
+ console.log("changed files:", files);
88
+ }
89
+ await spawn2`${options.packageManagerCommand} run ${options.buildDependenciesScript}`;
90
+ if (changeCount < 1) {
91
+ spawnedOnFirstBuild = options.onFirstBuild ? options.onFirstBuild() : startCommand();
92
+ }
93
+ changeCount++;
94
+ },
95
+ onTeardown: async () => {
96
+ if (spawnedOnFirstBuild) {
97
+ spawnedOnFirstBuild.kill();
98
+ }
99
+ await common.noopAsync();
100
+ }
101
+ }
102
+ ]
103
+ };
104
+ };
105
+ exports.normalizeTurbowatchLocalNodeModulesOptions = normalizeTurbowatchLocalNodeModulesOptions;
106
+ exports.turbowatchLocalNodeModules = turbowatchLocalNodeModules;
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './internal/watch-local-node-modules.js';
2
+ export * from './internal/watch-local-node-modules.options.js';
package/index.js ADDED
@@ -0,0 +1,106 @@
1
+ import { isNotNullish, noopAsync } from "@alexaegis/common";
2
+ import { getCurrentPackageRoot, NODE_MODULES_DIRECTORY_NAME, collectWorkspacePackages, collectIgnoreEntries } from "@alexaegis/workspace-tools";
3
+ import { spawn } from "node:child_process";
4
+ import { join } from "node:path";
5
+ import { normalizeCwdOption } from "@alexaegis/fs";
6
+ const normalizeTurbowatchLocalNodeModulesOptions = (options) => {
7
+ return {
8
+ ...normalizeCwdOption(options),
9
+ deep: options?.deep ?? true,
10
+ useGitIgnore: options?.useGitIgnore ?? false,
11
+ logChangedFiles: options?.logChangedFiles ?? false,
12
+ buildDependenciesScript: options?.buildDependenciesScript ?? "build:dependencies",
13
+ packageManagerCommand: options?.packageManagerCommand ?? "pnpm",
14
+ onFirstBuild: options?.onFirstBuild,
15
+ devScript: options?.devScript ?? "dev_"
16
+ };
17
+ };
18
+ const turbowatchLocalNodeModules = async (rawOptions) => {
19
+ const options = normalizeTurbowatchLocalNodeModulesOptions(rawOptions);
20
+ console.log("turbowatch started in", options.cwd);
21
+ const currentPackagePath = getCurrentPackageRoot(options.cwd);
22
+ if (!currentPackagePath) {
23
+ throw new Error("Not in a package!");
24
+ }
25
+ const currentPackagesNodeModulesPath = join(currentPackagePath, NODE_MODULES_DIRECTORY_NAME);
26
+ const workspacePackages = await collectWorkspacePackages({
27
+ ...options,
28
+ skipWorkspaceRoot: true
29
+ });
30
+ const doNotMatchPackageJson = ["not", ["match", "package.json", "basename"]];
31
+ const matchInLocalPackageDirectories = [
32
+ "anyof",
33
+ ...workspacePackages.map((workspacePackage) => workspacePackage.packageJson.name).filter(isNotNullish).map((packageName) => ["dirname", packageName])
34
+ ];
35
+ const commonIgnoredDirs = [
36
+ ["dirname", "dist"],
37
+ ["dirname", ".turbo"],
38
+ ["dirname", ".vercel"],
39
+ ["dirname", ".cache"],
40
+ ["dirname", "coverage"],
41
+ ["dirname", "build"],
42
+ ["match", "vite(st)?.config.*"]
43
+ ];
44
+ if (!options.deep) {
45
+ commonIgnoredDirs.push(["dirname", "node_modules"]);
46
+ }
47
+ const doNotMatchCommonOutputs = ["not", ["anyof", ...commonIgnoredDirs]];
48
+ const watchExpression = [
49
+ "allof",
50
+ matchInLocalPackageDirectories,
51
+ doNotMatchCommonOutputs,
52
+ doNotMatchPackageJson
53
+ ];
54
+ if (options.useGitIgnore) {
55
+ let ignoreEntries = await collectIgnoreEntries(options);
56
+ if (options.deep) {
57
+ ignoreEntries = ignoreEntries.filter(
58
+ (entry) => !entry.includes(NODE_MODULES_DIRECTORY_NAME)
59
+ );
60
+ }
61
+ const ignoreMatchEntries = ignoreEntries.map((ignoreEntry) => [
62
+ "match",
63
+ ignoreEntry
64
+ ]);
65
+ const doNotMatchIgnored = ["not", ["anyof", ...ignoreMatchEntries]];
66
+ watchExpression.push(doNotMatchIgnored);
67
+ }
68
+ let changeCount = 0;
69
+ const startCommand = () => {
70
+ return spawn(options.packageManagerCommand, ["run", options.devScript], {
71
+ stdio: "inherit"
72
+ });
73
+ };
74
+ let spawnedOnFirstBuild;
75
+ return {
76
+ project: currentPackagesNodeModulesPath,
77
+ debounce: { wait: 50 },
78
+ triggers: [
79
+ {
80
+ expression: watchExpression,
81
+ name: "build",
82
+ retry: { retries: 0 },
83
+ onChange: async ({ spawn: spawn2, files }) => {
84
+ if (options.logChangedFiles) {
85
+ console.log("changed files:", files);
86
+ }
87
+ await spawn2`${options.packageManagerCommand} run ${options.buildDependenciesScript}`;
88
+ if (changeCount < 1) {
89
+ spawnedOnFirstBuild = options.onFirstBuild ? options.onFirstBuild() : startCommand();
90
+ }
91
+ changeCount++;
92
+ },
93
+ onTeardown: async () => {
94
+ if (spawnedOnFirstBuild) {
95
+ spawnedOnFirstBuild.kill();
96
+ }
97
+ await noopAsync();
98
+ }
99
+ }
100
+ ]
101
+ };
102
+ };
103
+ export {
104
+ normalizeTurbowatchLocalNodeModulesOptions,
105
+ turbowatchLocalNodeModules
106
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.spec.d.ts","sourceRoot":"","sources":["../src/index.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import { type TurbowatchLocalNodeModulesOptions } from './watch-local-node-modules.options.js';
2
+ /**
3
+ * how about relying on CWD, and do the whole thing from a package?
4
+ */
5
+ export declare const turbowatchLocalNodeModules: (rawOptions?: TurbowatchLocalNodeModulesOptions) => Promise<Parameters<typeof import('turbowatch').watch>[0]>;
@@ -0,0 +1,69 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { Defined } from '@alexaegis/common';
3
+ import { type CwdOption } from '@alexaegis/fs';
4
+ import type { ChildProcess } from 'node:child_process';
5
+ export interface TurbowatchLocalNodeModulesOptions extends CwdOption {
6
+ /**
7
+ * If true, it will make sure ignore statements do not contain node_modules
8
+ * so the watcher can watch the entire dependency tree.
9
+ *
10
+ * If false deeper node_modules will be ignored.
11
+ *
12
+ * @default true
13
+ */
14
+ deep?: boolean | undefined;
15
+ /**
16
+ * If the default ignored files like
17
+ * - 'dist'
18
+ * - '.turbo'
19
+ * - '.vercel'
20
+ * - '.cache'
21
+ * - 'coverage'
22
+ * - 'build'
23
+ *
24
+ * Does not suffice, you can enable this and then it will read all the
25
+ * .gitignore files up your project root and include those too in the do
26
+ * not watch list. This could potentially ignore more than you actually
27
+ * want though!
28
+ *
29
+ * @default false
30
+ */
31
+ useGitIgnore?: boolean | undefined;
32
+ /**
33
+ * Log out changed files on every change. Useful for debugging if you
34
+ * notice builds are getting triggered over and over again.
35
+ *
36
+ * @default false
37
+ */
38
+ logChangedFiles?: boolean | undefined;
39
+ /**
40
+ * The command used to build the dependencies of this package.
41
+ *
42
+ * @default 'build:dependencies'
43
+ */
44
+ buildDependenciesScript?: string | undefined;
45
+ /**
46
+ * Which package manager to invoke when running buildDependenciesScript
47
+ *
48
+ * @default 'pnpm'
49
+ */
50
+ packageManagerCommand?: string | undefined;
51
+ /**
52
+ * Called the first time buildDependenciesScript finished
53
+ * You can use this to start your app if the way devScript doesn't suffice.
54
+ * If this is defined, devScript will not be used!
55
+ *
56
+ * It can be async but it won't be awaited!
57
+ */
58
+ onFirstBuild?: (() => ChildProcess | undefined) | undefined;
59
+ /**
60
+ * What package.json script should be started once buildDependenciesScript
61
+ * is first finished. By default it's `dev_` with an underscore at the
62
+ * end as this turbowatch call should be named `dev`
63
+ *
64
+ * @default 'dev_'
65
+ */
66
+ devScript?: string | undefined;
67
+ }
68
+ export type NormalizedTurbowatchLocalNodeModulesOptions = Defined<Omit<TurbowatchLocalNodeModulesOptions, 'onFirstBuild'>> & Pick<TurbowatchLocalNodeModulesOptions, 'onFirstBuild'>;
69
+ export declare const normalizeTurbowatchLocalNodeModulesOptions: (options?: TurbowatchLocalNodeModulesOptions) => NormalizedTurbowatchLocalNodeModulesOptions;
package/license ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Győri Sándor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const index = require("./index.cjs");
4
+ require("@alexaegis/common");
5
+ require("@alexaegis/workspace-tools");
6
+ require("node:child_process");
7
+ require("node:path");
8
+ require("@alexaegis/fs");
9
+ exports.turbowatchLocalNodeModules = index.turbowatchLocalNodeModules;
@@ -0,0 +1 @@
1
+ export * from './internal/watch-local-node-modules.js';
@@ -0,0 +1,9 @@
1
+ import { turbowatchLocalNodeModules } from "./index.js";
2
+ import "@alexaegis/common";
3
+ import "@alexaegis/workspace-tools";
4
+ import "node:child_process";
5
+ import "node:path";
6
+ import "@alexaegis/fs";
7
+ export {
8
+ turbowatchLocalNodeModules
9
+ };
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "@alexaegis/turbowatch",
3
+ "description": "A turbowatch configuration to watch local dependencies through node_modules",
4
+ "version": "0.7.0",
5
+ "license": "MIT",
6
+ "private": false,
7
+ "archetype": {
8
+ "platform": "node",
9
+ "language": "ts",
10
+ "kind": "lib"
11
+ },
12
+ "keywords": [
13
+ "managed-by-autotool",
14
+ "turbowatch"
15
+ ],
16
+ "author": {
17
+ "email": "alexaegis@gmail.com",
18
+ "name": "Alex Aegis",
19
+ "url": "https://github.com/AlexAegis"
20
+ },
21
+ "homepage": "https://github.com/AlexAegis/js-tooling",
22
+ "repository": {
23
+ "url": "git+https://github.com/AlexAegis/js-tooling",
24
+ "type": "git",
25
+ "directory": "packages/turbowatch"
26
+ },
27
+ "bugs": {
28
+ "email": "alexaegis@gmail.com",
29
+ "url": "https://github.com/AlexAegis/js-tooling/issues"
30
+ },
31
+ "type": "module",
32
+ "config": {
33
+ "engine-strict": true
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "engines": {
39
+ "node": ">=18.10.0",
40
+ "pnpm": ">=8.0.0"
41
+ },
42
+ "exports": {
43
+ ".": {
44
+ "types": "./index.d.ts",
45
+ "import": "./index.js",
46
+ "require": "./index.cjs",
47
+ "default": "./index.js"
48
+ },
49
+ "./local-node-modules": {
50
+ "types": "./local-node-modules.d.ts",
51
+ "import": "./local-node-modules.js",
52
+ "require": "./local-node-modules.cjs",
53
+ "default": "./local-node-modules.js"
54
+ },
55
+ "./readme": "./readme.md"
56
+ },
57
+ "dependencies": {
58
+ "@alexaegis/common": "^0.6.1",
59
+ "@alexaegis/fs": "^0.6.1",
60
+ "@alexaegis/workspace-tools": "^0.6.1"
61
+ },
62
+ "devDependencies": {
63
+ "@alexaegis/eslint-config-vitest": "^0.7.0",
64
+ "@alexaegis/ts": "^0.7.0",
65
+ "@alexaegis/vite": "^0.7.0",
66
+ "@alexaegis/vitest": "^0.7.0",
67
+ "@types/node": "^20.4.6",
68
+ "publint": "^0.2.0",
69
+ "turbowatch": "^2.29.4",
70
+ "typescript": "^5.1.6",
71
+ "vite": "^4.4.8",
72
+ "vitest": "^0.34.1"
73
+ },
74
+ "scripts": {
75
+ "build": "turbo run build-lib_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
76
+ "build-lib_": "vite build",
77
+ "lint:depcheck": "turbo run lint:depcheck_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
78
+ "lint:depcheck_": "depcheck",
79
+ "lint:es": "turbo run lint:es_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
80
+ "lint:es_": "eslint --max-warnings=0 --fix --no-error-on-unmatched-pattern .",
81
+ "lint:format": "turbo run lint:format_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
82
+ "lint:format_": "prettier --cache-location .cache/prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --check .",
83
+ "lint:md": "turbo run lint:md_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
84
+ "lint:md_": "remark --frail --no-stdout --silently-ignore .",
85
+ "lint:tsc": "turbo run lint:tsc_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
86
+ "lint:tsc_": "tsc --noEmit",
87
+ "publint": "BUILD_REASON='publish' turbo run publint_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
88
+ "publint_": "publint dist",
89
+ "all": "BUILD_REASON='publish' turbo run all_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
90
+ "format": "turbo run format_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
91
+ "format_": "prettier --cache-location .cache/prettier --plugin prettier-plugin-svelte --plugin prettier-plugin-tailwindcss --write .",
92
+ "test": "turbo run test_ --concurrency 16 --cache-dir .cache/turbo --filter @alexaegis/turbowatch",
93
+ "test_": "vitest --passWithNoTests --coverage --run",
94
+ "test:watch": "vitest --passWithNoTests --coverage"
95
+ }
96
+ }
package/readme.md ADDED
@@ -0,0 +1,97 @@
1
+ # [@alexaegis/turbowatch](https://github.com/AlexAegis/js-tooling/tree/master/packages/turbowatch)
2
+
3
+ [![npm](https://img.shields.io/npm/v/@alexaegis/turbowatch/latest)](https://www.npmjs.com/package/@alexaegis/turbowatch)
4
+ [![ci](https://github.com/AlexAegis/js-tooling/actions/workflows/cicd.yml/badge.svg)](https://github.com/AlexAegis/js-tooling/actions/workflows/cicd.yml)
5
+ [![codacy](https://app.codacy.com/project/badge/Grade/7939332dc9454dc1b0529e720ff902e6)](https://www.codacy.com/gh/AlexAegis/js-tooling/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AlexAegis/js-tooling&utm_campaign=Badge_Grade)
6
+
7
+ Holds a configuration for [`turbowatch`](https://github.com/gajus/turbowatch) to
8
+ watch local dependencies changing through `node_modules`.
9
+
10
+ It will first run `buildDependenciesScript`, then after that's finished the
11
+ first time, it will run `devScript`.
12
+
13
+ ## Turbowatch configuration example
14
+
15
+ > `apps/my-app/dev.js`
16
+
17
+ ```js
18
+ import { turbowatchLocalNodeModules } from '@alexaegis/turbowatch';
19
+ import { watch } from 'turbowatch';
20
+
21
+ void (async () => {
22
+ await watch(
23
+ await turbowatchLocalNodeModules({
24
+ buildDependenciesScript: 'build:dependencies',
25
+ devScript: 'dev_',
26
+ }),
27
+ );
28
+ })();
29
+ ```
30
+
31
+ > It's using an IIFE because turbowatch does not understand top-level awaits
32
+
33
+ ## Turbo example
34
+
35
+ When using with turbo, it's important that `turbo` cannot invoke itself. (In
36
+ older versions of turbo it was allowed but worked wonky, since then it's
37
+ actively prohibited) So when starting your app, start the invokation from
38
+ `turbowatch`
39
+
40
+ > `apps/my-app/package.json`
41
+
42
+ ```json
43
+ {
44
+ "scripts": {
45
+ "build:dependencies": "turbo run build-lib_ --filter my-app",
46
+ "dev": "turbowatch dev.js",
47
+ "dev_": "vite"
48
+ }
49
+ }
50
+ ```
51
+
52
+ And then the interesting part of a `turbo.json`. For the `turbowatch` setup
53
+ specifically, only the `build-lib_` task is used. Since `turbowatch` through the
54
+ `"build:dependencies"` npm script already takes care of building the
55
+ dependencies of the app, there's no need to start the app through `turbo`. For a
56
+ more comprehensive `turbo.json` file, check the one in this package's
57
+ repository.
58
+
59
+ > `turbo.json`
60
+
61
+ ```json
62
+ {
63
+ "$schema": "https://turborepo.org/schema.json",
64
+ "pipeline": {
65
+ "build-app_": {
66
+ "dependsOn": ["^build-lib_"],
67
+ "outputs": ["dist/**"]
68
+ },
69
+ "build-lib_": {
70
+ "dependsOn": ["^build-lib_"],
71
+ "outputs": ["dist/**"]
72
+ },
73
+ "dev_": {
74
+ "cache": false,
75
+ "dependsOn": ["^build-lib_"],
76
+ "persistent": true
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ > I'm using separate build tasks for apps and libs, so I can skip building the
83
+ > app when I'm running `turbo run build-lib_ --filter my-app`, if they'd use the
84
+ > same script, the app would get built every time a dependency changes.
85
+
86
+ You then start your app using the `"dev"` script. You can put one in your root
87
+ `package.json` too, but do not forget that you can't use `turbo` for this!
88
+
89
+ > `package.json`
90
+
91
+ ```json
92
+ {
93
+ "script": {
94
+ "dev": "pnpm run --dir apps/my-app dev"
95
+ }
96
+ }
97
+ ```