Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Backport tooling upgrades to support WarpDrive package unification #9755

Merged
merged 2 commits into from
Mar 10, 2025
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Code Of Conduct

The `ember-data` core team and and the broader `Ember` community are committed to everyone
The `warp-drive` core team and and the broader `Ember` community are committed to everyone
having a safe and inclusive experience.

- Our **Community Guidelines / Code of Conduct** can be found at [emberjs.com/guidelines](https://emberjs.com/guidelines/)
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Welcome!

We are so glad you are considering contributing to `EmberData`. Before that, kindly take a minute to read the [Code of Conduct](https://github.com/emberjs/data/blob/main/CODE_OF_CONDUCT.md). Below you'll find links to topics
We are so glad you are considering contributing to `WarpDrive`. Before that, kindly take a minute to read the [Code of Conduct](https://github.com/emberjs/data/blob/main/CODE_OF_CONDUCT.md). Below you'll find links to topics
detailing how to become involved to best ensure your contributions are successful!

- [Reporting Issues](./contributing/issues.md)
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017-2024 Ember.js and contributors
Copyright (c) 2017-2025 Ember.js and contributors
Copyright (c) 2011-2017 Tilde, Inc. and contributors
Copyright (c) 2011 LivingSocial Inc.

Expand Down
28 changes: 28 additions & 0 deletions internal-tooling/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# @warp-drive/internal-tooling

This internal (private) project provides a number of tooling scripts
for use with the monorepo.

These scripts can be run as bin-scripts from root.

## bun sync-logos

Will sync the logo directory from root to each public package and
ensure that the logos directory is included in published files.

## bun sync-license

Will sync the LICENSE.md file from root to each public package and
ensure that the license is both set to `MIT` in the package.json and
included in the published files for each package.

## bun sync-references

Will ensure that `paths` and `references` are both correctly specified
in tsconfig.json for any other workspace package specified by package.json
as a dependency, peer-dependency, or dev-dependency.

Will also ensure the proper settings for composite etc. are in use.

For packages that should emit types (any non-test app package) it will
ensure that the declarationDir is added to the files array in package.json.
32 changes: 32 additions & 0 deletions internal-tooling/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@warp-drive/internal-tooling",
"version": "5.4.0-alpha.142",
"description": "Internal Tooling for the WarpDrive Project Monorepo | Unpublished",
"private": true,
"type": "module",
"files": [
"src"
],
"bin": {
"sync-logos": "src/sync-logos.ts",
"sync-license": "src/sync-license.ts",
"sync-references": "src/sync-references.ts"
},
"dependencies": {
"@types/bun": "^1.2.4",
"typescript": "^5.8.2",
"chalk": "5.4.1",
"debug": "4.4.0",
"@types/debug": "4.1.12",
"comment-json": "^4.2.5",
"@pnpm/find-workspace-dir": "1000.1.0",
"@pnpm/find-workspace-packages": "6.0.9",
"@pnpm/logger": "1000.0.0"
},
"engines": {
"node": ">= 18.20.7"
},
"volta": {
"extends": "../package.json"
}
}
201 changes: 201 additions & 0 deletions internal-tooling/src/-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import type { BunFile } from 'bun';
import path from 'path';
import type { CommentObject } from 'comment-json';
import { findWorkspaceDir } from '@pnpm/find-workspace-dir';
import { findWorkspacePackages, type Project } from '@pnpm/find-workspace-packages';

export async function getMonorepoRoot() {
const workspaceDir = await findWorkspaceDir(process.cwd());

if (workspaceDir) {
return workspaceDir;
}

const MAX_DEPTH = 10;
// are we in the root?
let currentDir = process.cwd();
let depth = 0;
while (depth < MAX_DEPTH) {
const lockfileFile = path.join(currentDir, 'pnpm-lock.yaml');
if (await Bun.file(lockfileFile).exists()) {
return currentDir;
}
currentDir = path.join(currentDir, '../');
depth++;
}

throw new Error(`Could not find monorepo root from cwd ${process.cwd()}`);
}

export async function getPackageJson({ packageDir, packagesDir }: { packageDir: string; packagesDir: string }) {
const packageJsonPath = path.join(packagesDir, packageDir, 'package.json');
const packageJsonFile = Bun.file(packageJsonPath);
const pkg = await packageJsonFile.json();
return { file: packageJsonFile, pkg, path: packageJsonPath, nicePath: path.join(packageDir, 'package.json') };
}

export async function runPrettier() {
const root = await getMonorepoRoot();
const childProcess = Bun.spawn(['bun', 'lint:prettier:fix'], {
env: process.env,
cwd: root,
stdout: 'inherit',
stderr: 'inherit',
});
await childProcess.exited;
}

type PkgJsonFile = {
name: string;
version: string;
files?: string[];
license?: string;
private?: boolean;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
scripts?: Record<string, string>;
main?: string;
peerDependenciesMeta?: Record<string, { optional: boolean }>;
};

export type TsConfigFile = {
include?: string[];
compilerOptions?: {
lib?: string[];
module?: string;
target?: string;
moduleResolution?: string;
moduleDetection?: string;
erasableSyntaxOnly?: boolean;
allowImportingTsExtensions?: boolean;
verbatimModuleSyntax?: boolean;
isolatedModules?: boolean;
isolatedDeclarations?: boolean;
pretty?: boolean;
strict?: boolean;
experimentalDecorators?: boolean;
allowJs?: boolean;
checkJs?: boolean;
rootDir?: string;
baseUrl?: string;
declarationMap?: boolean;
inlineSourceMap?: boolean;
inlineSources?: boolean;
skipLibCheck?: boolean;
declaration?: boolean;
declarationDir?: string;
incremental?: boolean;
composite?: boolean;
emitDeclarationOnly?: boolean;
noEmit?: boolean;
paths?: Record<string, string[]>;
types?: string[];
};
references?: { path: string }[];
};

interface BaseProjectPackage {
project: Project;
packages: Map<string, Project>;
pkgFile: BunFile;
tsconfigFile: BunFile;
pkgPath: string;
tsconfigPath: string;
isRoot: boolean;
isPrivate: boolean;
isTooling: boolean;
isConfig: boolean;
isTest: boolean;
pkg: PkgJsonFile;
save: (editStatus: { pkgEdited: boolean; configEdited: Boolean }) => Promise<void>;
}

export interface ProjectPackageWithTsConfig extends BaseProjectPackage {
tsconfig: CommentObject & TsConfigFile;
hasTsConfig: true;
}

interface ProjectPackageWithoutTsConfig extends BaseProjectPackage {
tsconfig: null;
hasTsConfig: false;
}

export type ProjectPackage = ProjectPackageWithTsConfig | ProjectPackageWithoutTsConfig;

async function collectAllPackages(dir: string) {
const packages = await findWorkspacePackages(dir);
const pkgMap = new Map<string, Project>();
for (const pkg of packages) {
if (!pkg.manifest.name) {
throw new Error(`Package at ${pkg.dir} does not have a name`);
}
pkgMap.set(pkg.manifest.name, pkg);
}

return pkgMap;
}

export async function walkPackages(
cb: (pkg: ProjectPackage, projects: Map<string, ProjectPackage>) => void | Promise<void>,
options: {
excludeTests?: boolean;
excludePrivate?: boolean;
excludeRoot?: boolean;
excludeTooling?: boolean;
excludeConfig?: boolean;
} = {}
) {
const config = Object.assign(
{ excludeTests: false, excludePrivate: false, excludeRoot: true, excludeTooling: true, excludeConfig: true },
options
);
const JSONC = await import('comment-json');
const dir = await getMonorepoRoot();
const packages = await collectAllPackages(dir);
const projects = new Map<string, ProjectPackageWithTsConfig>();
const TestDir = path.join(dir, 'tests');

for (const [name, project] of packages) {
if (config.excludeRoot && name === 'root') continue;
if (config.excludePrivate && project.manifest.private) continue;
if (config.excludeTooling && name === '@warp-drive/internal-tooling') continue;
if (config.excludeConfig && name === '@warp-drive/config') continue;
if (config.excludeTests && project.dir.startsWith(TestDir)) continue;

const pkgPath = path.join(project.dir, 'package.json');
const tsconfigPath = path.join(project.dir, 'tsconfig.json');
const pkgFile = Bun.file(pkgPath);
const tsconfigFile = Bun.file(tsconfigPath);
const pkg = (await pkgFile.json()) as PkgJsonFile;
const hasTsConfig = await tsconfigFile.exists();
const tsconfig = hasTsConfig ? (JSONC.parse(await tsconfigFile.text()) as CommentObject & TsConfigFile) : null;

const pkgObj = {
project,
packages,
pkgFile,
tsconfigFile,
pkgPath,
hasTsConfig,
tsconfigPath,
isRoot: name === 'root',
isPrivate: project.manifest.private ?? false,
isTooling: name === '@warp-drive/internal-tooling',
isConfig: name === '@warp-drive/config',
isTest: project.dir.startsWith(TestDir),
pkg,
tsconfig,
save: async ({ pkgEdited, configEdited }: { pkgEdited: boolean; configEdited: Boolean }) => {
if (pkgEdited) await pkgFile.write(JSON.stringify(pkg, null, 2));
if (configEdited) await tsconfigFile.write(JSONC.stringify(tsconfig, null, 2));
},
} as ProjectPackageWithTsConfig;

projects.set(name, pkgObj);
}

for (const project of projects.values()) {
await cb(project, projects);
}
}
74 changes: 74 additions & 0 deletions internal-tooling/src/sync-license.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#! /usr/bin/env bun

import path from 'path';
import fs from 'fs';
import debug from 'debug';
import chalk from 'chalk';
import type { BunFile } from 'bun';
import { getMonorepoRoot, getPackageJson } from './-utils';

const log = debug('wd:sync-license');

async function updatePackageJson({ pkg, file, nicePath }: { pkg: any; file: BunFile; path: string; nicePath: string }) {
let edited = false;
// ensure "files" field in package.json includes "LICENSE.md"
if (!pkg.files) {
pkg.files = ['LICENSE.md'];
edited = true;
log(`\t\t📝 Added "LICENSE.md" to "files" in ${nicePath}`);
} else if (!pkg.files.includes('LICENSE.md')) {
pkg.files.push('LICENSE.md');
edited = true;
log(`\t\t📝 Added "LICENSE.md" to "files" in ${nicePath}`);
}

if (pkg.license !== 'MIT') {
pkg.license = 'MIT';
edited = true;
log(`\t\t⚖️ Updated "license" to "MIT" in ${nicePath}`);
}

if (edited) {
await file.write(JSON.stringify(pkg, null, 2));
}
}

async function main() {
log(
`\n\t${chalk.gray('=').repeat(60)}\n\t\t${chalk.magentaBright('@warp-drive/')}${chalk.greenBright('internal-tooling')} Sync LICENSE.md\n\t${chalk.gray('=').repeat(60)}\n\n\t\t${chalk.gray(`Syncing LICENSE.md from monorepo root to each public package`)}\n\n`
);
const monorepoRoot = await getMonorepoRoot();

// sync the LICENSE.md file from the monorepo root to each
// public package

const licenseFilePath = path.join(monorepoRoot, 'LICENSE.md');
const packagesDir = path.join(monorepoRoot, 'packages');

for (const packageDir of fs.readdirSync(packagesDir)) {
const details = await getPackageJson({ packageDir, packagesDir });

if (details.pkg.private) {
log(`\t\t🔒 Skipping private package ${details.nicePath}`);
continue;
}

const packageFullDir = path.join(packagesDir, packageDir);
const packageLicensePath = path.join(packageFullDir, 'LICENSE.md');

// remove th existing LICENSE.md file if it exists
if (fs.existsSync(packageLicensePath)) {
fs.rmSync(packageLicensePath);
log(`\t\t💨 Deleted existing LICENSE.md in ${packageDir}`);
}

fs.copyFileSync(licenseFilePath, packageLicensePath);
log(`\t\t⚖️ Copied LICENSE.md to ${packageDir}`);

await updatePackageJson(details);

log('\n');
}
}

main();
Loading
Loading