Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/ten-ducks-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@baseplate-dev/project-builder-server': patch
'@baseplate-dev/project-builder-cli': patch
'@baseplate-dev/project-builder-lib': patch
'@baseplate-dev/project-builder-web': patch
'@baseplate-dev/fastify-generators': patch
'@baseplate-dev/react-generators': patch
'@baseplate-dev/core-generators': patch
'@baseplate-dev/create-project': patch
'@baseplate-dev/ui-components': patch
'@baseplate-dev/plugin-storage': patch
'@baseplate-dev/plugin-auth': patch
'@baseplate-dev/utils': patch
'@baseplate-dev/sync': patch
---

Switch to Typescript project references for building/watching project
151 changes: 151 additions & 0 deletions .workspace-meta/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import type { PackageJson } from 'workspace-meta';

import { isEqual, merge } from 'es-toolkit';
import { isMatch } from 'es-toolkit/compat';
import { readFileSync } from 'node:fs';
import path from 'node:path';
import {
Expand All @@ -7,7 +11,42 @@ import {
prettierFormatter,
} from 'workspace-meta';

function getProjectJsonDependencyKeys(packageJson: PackageJson): string[] {
return [
...Object.keys(packageJson.dependencies ?? {}),
...Object.keys(packageJson.devDependencies ?? {}),
];
}

function isPluginPackage(packageJson: PackageJson): boolean {
return packageJson.name?.startsWith('@baseplate-dev/plugin-') ?? false;
}

// False positives for the Typescript project references
const IGNORED_PACKAGES = new Set([
'@baseplate-dev/project-builder-web',
'@baseplate-dev/root',
]);

interface TsconfigBuild {
compilerOptions:
| {
tsBuildInfoFile?: string;
composite?: boolean;
incremental?: boolean;
rootDir?: string;
outDir?: string;
}
| undefined;
references:
| {
path: string;
}[]
| undefined;
}

export default defineWorkspaceMetaConfig({
includeRootPackage: true,
formatter: (content, filename) => {
if (filename.endsWith('LICENSE')) {
return content;
Expand All @@ -33,9 +72,121 @@ export default defineWorkspaceMetaConfig({
access: 'public',
provenance: true,
} as { access: 'public' };

if (packageJson.scripts?.build) {
const hasTemplatesFolder =
packageJson.files?.includes('templates/**/*');
packageJson.files = [
'README.md',
'LICENSE',
'CHANGELOG',
'dist/**/*',
'!dist/**/*.d.ts.map',
'!dist/**/*.tsbuildinfo',
];

// If we have a templates directory, make sure we keep it
if (hasTemplatesFolder) {
packageJson.files.push('templates/**/*');
}

// If we have a bin, make sure we add it
if (packageJson.bin) {
packageJson.files.push('bin/**/*');
}

if (isPluginPackage(packageJson))
packageJson.files.push('manifest.json');
}
}

return packageJson;
}),
// attach project references for tsconfig.build.json
async (ctx) => {
const dependencies = getProjectJsonDependencyKeys(ctx.packageJson);

if (!dependencies.includes('typescript')) {
return;
}

const isRootPackage = ctx.packagePath === ctx.workspacePath;

const projectDependencyNames = isRootPackage
? ctx.workspacePackages.map((p) => p.name)
: getProjectJsonDependencyKeys(ctx.packageJson);

const interProjectDependencies = projectDependencyNames
.filter((name) => !IGNORED_PACKAGES.has(name))
.map((name) => {
const packageInfo = ctx.workspacePackages.find(
(p) => p.name === name,
);
if (!packageInfo) {
return undefined;
}

if (
getProjectJsonDependencyKeys(packageInfo.packageJson).includes(
'typescript',
) &&
Object.keys(packageInfo.packageJson.scripts ?? {}).includes('build')
) {
const relativePath = path.join(
path.relative(ctx.packagePath, packageInfo.path),
'tsconfig.build.json',
);
return relativePath.startsWith('.')
? relativePath
: `./${relativePath}`;
}
})
.filter((name) => name !== undefined)
.toSorted();

const projectReferences = interProjectDependencies.map((filePath) => ({
path: filePath,
}));

const tsconfigBuild = await ctx.readFile('tsconfig.build.json');
if (!tsconfigBuild) {
return;
}

const parsedTsconfig = JSON.parse(tsconfigBuild) as TsconfigBuild;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for JSON parsing.

The JSON.parse operation could throw an error if the tsconfig.build.json file contains malformed JSON, which would cause the plugin to fail.

-      const parsedTsconfig = JSON.parse(tsconfigBuild) as TsconfigBuild;
+      let parsedTsconfig: TsconfigBuild;
+      try {
+        parsedTsconfig = JSON.parse(tsconfigBuild) as TsconfigBuild;
+      } catch (error) {
+        console.warn(`Failed to parse tsconfig.build.json in ${ctx.packagePath}:`, error);
+        return;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const parsedTsconfig = JSON.parse(tsconfigBuild) as TsconfigBuild;
let parsedTsconfig: TsconfigBuild;
try {
parsedTsconfig = JSON.parse(tsconfigBuild) as TsconfigBuild;
} catch (error) {
console.warn(`Failed to parse tsconfig.build.json in ${ctx.packagePath}:`, error);
return;
}
🤖 Prompt for AI Agents
In .workspace-meta/config.ts at line 126, the JSON.parse call on tsconfigBuild
can throw an error if the JSON is malformed. Wrap the JSON.parse operation in a
try-catch block to catch any parsing errors. In the catch block, handle the
error gracefully by logging an appropriate error message or taking corrective
action to prevent the plugin from failing unexpectedly.


const targetConfig: TsconfigBuild = {
compilerOptions: {
tsBuildInfoFile: './dist/tsconfig.build.tsbuildinfo',
composite: true,
incremental: true,
rootDir: 'src',
outDir: 'dist',
},
references: projectReferences,
};

if (isRootPackage) {
targetConfig.compilerOptions = undefined;
}

if (
isMatch(parsedTsconfig.compilerOptions, targetConfig.compilerOptions) &&
isEqual(parsedTsconfig.references, targetConfig.references)
) {
return;
}

parsedTsconfig.references = targetConfig.references;
parsedTsconfig.compilerOptions ??= {};
if (targetConfig.compilerOptions) {
merge(parsedTsconfig.compilerOptions, targetConfig.compilerOptions);
}

await ctx.writeFile(
'tsconfig.build.json',
JSON.stringify(parsedTsconfig, null, 2),
);
},
],
});
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@baseplate-dev/root",
"private": true,
"license": "SEE LICENSE IN LICENSE",
"homepage": "https://www.baseplate.dev",
"license": "MPL-2.0",
"author": "Half Dome Labs LLC",
"type": "module",
"scripts": {
"build": "turbo run build",
"build:affected": "turbo run build --affected",
"build:storybook": "turbo run build:storybook --filter=@baseplate-dev/ui-components",
"changesets:check": "node --experimental-strip-types ./scripts/check-changesets.ts",
"clean": "turbo run clean",
"dev:serve": "pnpm --filter @baseplate-dev/project-builder-cli dev:serve",
Expand All @@ -30,6 +30,7 @@
"release": "pnpm build && pnpm changeset publish",
"run:morpher": "pnpm --filter @baseplate-dev/code-morph run:morpher",
"serve": "turbo run @baseplate-dev/project-builder-cli:start serve",
"storybook:build": "turbo run storybook:build --filter=@baseplate-dev/ui-components",
"test": "turbo run test --continue",
"test:affected": "turbo run test --affected --continue",
"test:e2e": "turbo run test:e2e --continue",
Expand All @@ -39,7 +40,8 @@
"typecheck": "turbo run typecheck --affected",
"versions:check": "check-dependency-version-consistency .",
"versions:fix": "check-dependency-version-consistency . --fix",
"watch": "turbo run watch --concurrency 20"
"watch": "turbo run watch:tsc:root watch --concurrency 20",
"watch:tsc:root": "tsc -b tsconfig.build.json --preserveWatchOutput -w"
},
"lint-staged": {
"*.(js|ts|tsx|jsx|json|md|mdx|css|scss|yaml|yml|toml|html|svg|yml|yaml|json)": "prettier --write"
Expand All @@ -52,6 +54,7 @@
"@changesets/changelog-github": "0.5.1",
"@types/node": "catalog:",
"check-dependency-version-consistency": "5.0.0",
"es-toolkit": "1.31.0",
"eslint": "catalog:",
"husky": "9.1.7",
"knip": "5.59.0",
Expand All @@ -60,7 +63,7 @@
"turbo": "2.5.0",
"typescript": "catalog:",
"vitest": "catalog:",
"workspace-meta": "0.1.2"
"workspace-meta": "0.1.4"
},
"packageManager": "pnpm@10.6.5",
"engines": {
Expand Down
12 changes: 6 additions & 6 deletions packages/core-generators/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
},
"main": "dist/index.js",
"files": [
"README.md",
"LICENSE",
"CHANGELOG",
"dist/**/*",
"!dist/**/*.d.ts.map",
"!dist/**/*.tsbuildinfo",
"CHANGELOG"
"!dist/**/*.tsbuildinfo"
],
"scripts": {
"build": "concurrently pnpm:build:*",
Expand All @@ -43,10 +44,9 @@
"prettier:write": "prettier -w .",
"test": "vitest",
"test:coverage": "vitest run --coverage",
"tsc:watch": "tsc -p tsconfig.build.json --preserveWatchOutput -w",
"typecheck": "tsc --noEmit",
"watch": "concurrently pnpm:watch:*",
"watch:templates": "pnpm build:templates --watch",
"watch:tsc": "tsc -p tsconfig.build.json --preserveWatchOutput -w"
"watch": "pnpm build:templates --watch"
},
"dependencies": {
"@baseplate-dev/sync": "workspace:*",
Expand All @@ -70,7 +70,7 @@
"@types/prettier": "^2.7.3",
"@types/semver": "^7.5.0",
"concurrently": "9.0.1",
"cpx2": "7.0.1",
"cpx2": "catalog:",
"eslint": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
Expand Down
8 changes: 8 additions & 0 deletions packages/core-generators/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,13 @@
"src/**/*.test-helper.ts",
"**/__mocks__/**/*",
"**/generators/*/*/templates/**"
],
"references": [
{
"path": "../sync/tsconfig.build.json"
},
{
"path": "../utils/tsconfig.build.json"
}
]
}
3 changes: 3 additions & 0 deletions packages/create-project/bin/create-baseplate-project.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

import '../dist/create-baseplate-project.js';
9 changes: 5 additions & 4 deletions packages/create-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
"default": "./dist/*"
}
},
"bin": "./dist/create-baseplate-project.js",
"bin": "./bin/create-baseplate-project.js",
"files": [
"README.md",
"LICENSE",
"CHANGELOG",
"dist/**/*",
"scripts/**/*",
"templates/**/*",
"!dist/**/*.d.ts.map",
"!dist/**/*.tsbuildinfo",
"README.md"
"templates/**/*",
"bin/**/*"
],
"scripts": {
"build": "tsc -p tsconfig.build.json",
Expand Down
5 changes: 5 additions & 0 deletions packages/create-project/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@
"src/**/*.test-helper.ts",
"**/__mocks__/**/*",
"**/generators/*/*/templates/**"
],
"references": [
{
"path": "../utils/tsconfig.build.json"
}
]
}
11 changes: 6 additions & 5 deletions packages/fastify-generators/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
},
"main": "dist/index.js",
"files": [
"README.md",
"LICENSE",
"CHANGELOG",
"dist/**/*",
"!dist/**/*.d.ts.map",
"!dist/**/*.tsbuildinfo",
"CHANGELOG"
"!dist/**/*.tsbuildinfo"
],
"scripts": {
"build": "concurrently pnpm:build:*",
Expand All @@ -31,10 +32,10 @@
"lint": "eslint .",
"prettier:check": "prettier --check .",
"prettier:write": "prettier -w .",
"tsc:watch": "tsc -p tsconfig.build.json --preserveWatchOutput -w",
"typecheck": "tsc --noEmit",
"watch": "concurrently pnpm:watch:*",
"watch:templates": "pnpm build:templates --watch",
"watch:tsc": "tsc -p tsconfig.build.json --preserveWatchOutput -w"
"watch:templates": "pnpm build:templates --watch"
},
"dependencies": {
"@baseplate-dev/core-generators": "workspace:*",
Expand All @@ -51,7 +52,7 @@
"@baseplate-dev/tools": "workspace:*",
"@tailwindcss/forms": "0.5.9",
"concurrently": "9.0.1",
"cpx2": "7.0.1",
"cpx2": "catalog:",
"eslint": "catalog:",
"prettier": "catalog:",
"typescript": "catalog:"
Expand Down
11 changes: 11 additions & 0 deletions packages/fastify-generators/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,16 @@
"src/**/*.test-helper.ts",
"**/__mocks__/**/*",
"**/generators/*/*/templates/**"
],
"references": [
{
"path": "../core-generators/tsconfig.build.json"
},
{
"path": "../sync/tsconfig.build.json"
},
{
"path": "../utils/tsconfig.build.json"
}
]
}
3 changes: 3 additions & 0 deletions packages/project-builder-cli/bin/baseplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

import '../dist/cli.js';
Loading