Skip to content

Commit

Permalink
chore: differentiate external vs local modules (#160)
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFlurry committed Mar 7, 2024
2 parents 5507e8f + 1dd2bfa commit 36705d2
Show file tree
Hide file tree
Showing 8 changed files with 1,455 additions and 51 deletions.
1,385 changes: 1,385 additions & 0 deletions deno.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/build/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,12 @@ async function buildSteps(
// TODO: Also watch migrations folder in case a migration is created/destroyed
files: [resolve(module.path, "db", "schema.prisma")],
async build() {
if ("local" in module.registry.config) {
// Update migrations
await migrateDev(project, [module], { createOnly: false });
} else {
if (module.registry.isExternal) {
// Do not alter migrations, only deploy them
await migrateDeploy(project, [module]);
} else {
// Update migrations
await migrateDev(project, [module], { createOnly: false });
}

// Generate client
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const testCommand = new Command<GlobalOpts>()
// Filter modules
if (modulesFilter.length == 0) {
// Only test local modules
if (!("local" in module.registry.config)) continue;
if (module.registry.isExternal) continue;
} else {
// Only test specified modules. This allows for testing remote modules.
if (!modulesFilter.includes(module.name)) continue;
Expand Down
9 changes: 9 additions & 0 deletions src/config/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ export type RegistryConfig = { local: RegistryConfigLocal } | { git: RegistryCon

export interface RegistryConfigLocal {
directory: string;

/**
* If true, this will be treated like an external registry. This is
* important if multiple projects are using the same registry locally.
*
* Modules from this directory will not be tested, formatted, linted, and
* generate Prisma migrations.
*/
isExternal?: boolean;
}

export type RegistryConfigGit = { url: RegistryConfigGitUrl, directory?: string } & ({ branch: string } | { tag: string } | { rev: string });
Expand Down
6 changes: 1 addition & 5 deletions src/migrate/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function migrateDev(
modules: Module[],
opts: MigrateDevOpts,
) {
assert(modules.every(m => ("local" in m.registry.config)), "Only local modules can run migrateDev because it generates migration files");
assert(modules.every(m => !m.registry.isExternal), "Only modules from local registries can run migrateDev because it generates migration files");

await forEachPrismaSchema(
project,
Expand Down Expand Up @@ -53,10 +53,6 @@ export async function migrateDev(
const migrationsDir = resolve(module.path, "db", "migrations");
await emptyDir(migrationsDir);
await copy(tempMigrationsDir, migrationsDir, { overwrite: true });

const sourceMigrationsDir = resolve(module.sourcePath, "db", "migrations");
await emptyDir(sourceMigrationsDir);
await copy(tempMigrationsDir, sourceMigrationsDir, { overwrite: true });
}
},
);
Expand Down
11 changes: 0 additions & 11 deletions src/project/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,6 @@ export interface Module {
* This path can be modified and will be discarded on the next codegen.
*/
path: string;

/**
* The path to the module's source code.
*
* This path almost never be modified (including _gen), except for
* exclusions where auto-generating code (e.g. prisma migrate dev).
*/
sourcePath: string;

name: string;
config: ModuleConfig;
registry: Registry,
Expand All @@ -37,7 +28,6 @@ export interface ModuleDatabase {

export async function loadModule(
modulePath: string,
sourcePath: string,
name: string,
registry: Registry,
): Promise<Module> {
Expand Down Expand Up @@ -110,7 +100,6 @@ export async function loadModule(

return {
path: modulePath,
sourcePath,
name,
config,
registry,
Expand Down
48 changes: 27 additions & 21 deletions src/project/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ export async function loadProject(opts: LoadProjectOpts): Promise<Project> {
// Load modules
const modules = new Map<string, Module>();
for (const projectModuleName in projectConfig.modules) {
const { genPath, sourcePath, registry } = await fetchAndResolveModule(
const { path, registry } = await fetchAndResolveModule(
projectRoot,
projectConfig,
registries,
projectModuleName,
);
const module = await loadModule(genPath, sourcePath, projectModuleName, registry);
const module = await loadModule(path, projectModuleName, registry);
modules.set(projectModuleName, module);
}

Expand All @@ -91,7 +91,7 @@ export async function loadProject(opts: LoadProjectOpts): Promise<Project> {
if (missingDepsByModule.size > 0) {
let message = bold(brightRed("Unresolved module dependencies:\n"));
for (const [moduleName, missingDeps] of missingDepsByModule) {
message += `\tModule ${moduleName} is missing dependencies: ${
message += `\tCannot resolve dependencies for ${moduleName}: ${
missingDeps.join(", ")
}\n`;
}
Expand Down Expand Up @@ -121,7 +121,7 @@ interface FetchAndResolveModuleOutput {
/**
* Path the module was copied to in _gen.
*/
genPath: string;
path: string;

/**
* Path to the original module source code.
Expand Down Expand Up @@ -161,8 +161,8 @@ async function fetchAndResolveModule(

// Resolve module path
const pathModuleName = moduleNameInRegistry(moduleName, module);
const modulePath = resolve(registry.path, pathModuleName);
if (!await exists(resolve(modulePath, "module.yaml"))) {
const sourcePath = resolve(registry.path, pathModuleName);
if (!await exists(resolve(sourcePath, "module.yaml"))) {
if (pathModuleName != moduleName) {
// Has alias
throw new Error(
Expand All @@ -176,21 +176,27 @@ async function fetchAndResolveModule(
}
}

// Copy to gen dir
//
// We do this so generate files into the gen dir without modifying the
// original module. For example. if multiple projects are using the same
// local registry, we don't want conflicting generated files.
const dstPath = resolve(
projectRoot,
"_gen",
"modules",
moduleName,
);
await emptyDir(dstPath);
await copy(modulePath, dstPath, { overwrite: true });
let path: string;
if (registry.isExternal) {
// Copy to gen dir
//
// We do this so generate files into the gen dir without modifying the
// original module. For example. if multiple projects are using the same
// local registry, we don't want conflicting generated files.
path = resolve(
projectRoot,
"_gen",
"external_modules",
moduleName,
);
await emptyDir(path);
await copy(sourcePath, path, { overwrite: true });
} else {
// Use original path
path = sourcePath;
}

return { genPath: dstPath, sourcePath: modulePath, registry };
return { path, sourcePath: sourcePath, registry };
}

function registryNameForModule(module: ProjectModuleConfig): string {
Expand Down Expand Up @@ -231,7 +237,7 @@ export async function listSourceFiles(
const files: string[] = [];
for (const module of project.modules.values()) {
// Skip non-local files
if (opts.localOnly && !("local" in module.registry.config)) continue;
if (opts.localOnly && module.registry.isExternal) continue;

const moduleFiles =
(await glob.glob("**/*.ts", { cwd: module.path, ignore: "_gen/**" }))
Expand Down
37 changes: 28 additions & 9 deletions src/project/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ export interface Registry {
path: string;
name: string;
config: RegistryConfig;

/**
* If the source code for this registry does not belong to this project.
*
* If true, modules will be copied to the _gen dir and will be read-only.
*
* If this is true, the module should be treated as read-only and should not
* be tested, formatted, linted, and generate Prisma migrations.
*/
isExternal: boolean;
}

/**
Expand All @@ -19,40 +29,49 @@ export async function loadRegistry(
name: string,
config: RegistryConfig,
): Promise<Registry> {
let path: string;
let output: ResolveRegistryOutput;
if ("local" in config) {
path = await resolveRegistryLocal(projectRoot, config.local);
output = await resolveRegistryLocal(projectRoot, config.local);
} else if ("git" in config) {
path = await resolveRegistryGit(projectRoot, name, config.git);
output = await resolveRegistryGit(projectRoot, name, config.git);
} else {
// Unknown project config
throw new Error("Unreachable");
}

return {
path,
path: output.path,
name,
config,
isExternal: output.isExternal,
};
}

interface ResolveRegistryOutput {
path: string;
isExternal: boolean;
}

async function resolveRegistryLocal(
projectRoot: string,
config: RegistryConfigLocal,
): Promise<string> {
): Promise<ResolveRegistryOutput> {
const isExternal = config.isExternal ?? false;

// Check that registry exists
const path = resolve(projectRoot, config.directory);
if (!await exists(path)) {
throw new Error(`Registry not found at ${path}`);
}
return path;

return { path, isExternal };
}

async function resolveRegistryGit(
projectRoot: string,
name: string,
config: RegistryConfigGit,
): Promise<string> {
): Promise<ResolveRegistryOutput> {
const repoPath = resolve(projectRoot, "_gen", "git_registries", name);
const gitRef = resolveGitRef(config);

Expand Down Expand Up @@ -169,7 +188,7 @@ async function resolveRegistryGit(
throw new Error(`Registry not found at ${path}`);
}

return path;
return { path, isExternal: true };
}

function resolveGitRef(registryConfig: RegistryConfigGit): string {
Expand All @@ -182,4 +201,4 @@ function resolveGitRef(registryConfig: RegistryConfigGit): string {
} else {
throw new Error("Unreachable");
}
}
}

0 comments on commit 36705d2

Please sign in to comment.