Skip to content

Commit

Permalink
feat: working eject command straight from chimp
Browse files Browse the repository at this point in the history
  • Loading branch information
lgandecki committed Aug 22, 2023
1 parent a9a303a commit e3341e8
Show file tree
Hide file tree
Showing 3 changed files with 300 additions and 3 deletions.
150 changes: 149 additions & 1 deletion oclif.manifest.json
Original file line number Diff line number Diff line change
@@ -1 +1,149 @@
{"version":"0.0.0-development","commands":{"create":{"id":"create","description":"describe the command here","pluginName":"chimp","pluginType":"core","aliases":[],"examples":["$ chimp create my-new-app","$ chimp create my-new-app -a ~src -g ~chimp-helpers"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"appPrefix":{"name":"appPrefix","type":"option","char":"a","description":"prefix that points to the sourcecode of your app","default":"~app"},"generatedPrefix":{"name":"generatedPrefix","type":"option","char":"g","description":"prefix that points to the generated by chimp helper code","default":"~generated"}},"args":[{"name":"name","description":"name of the new app, also used as the directory"}]},"generate":{"id":"generate","description":"generate GraphQL code","pluginName":"chimp","pluginType":"core","aliases":[],"examples":["$ chimp generate","$ chimp generate -a ~src -g ~chimp-helpers"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"appPrefix":{"name":"appPrefix","type":"option","char":"a","description":"prefix that points to the sourcecode of your app","default":"~app"},"generatedPrefix":{"name":"generatedPrefix","type":"option","char":"g","description":"prefix that points to the generated by chimp helper code","default":"~generated"},"modulesPath":{"name":"modulesPath","type":"option","char":"p","description":"path to the graphQL modules, only use if you are migrating an existing Apollo App and you want to use chimp only for a part of it"}},"args":[]},"init":{"id":"init","description":"init Chimp","pluginName":"chimp","pluginType":"core","aliases":[],"examples":["$ chimp init","$ chimp init -p ./src/chimp-modules"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"modulesPath":{"name":"modulesPath","type":"option","char":"p","description":"path to the GraphQL modules.","default":"./src/modules"}},"args":[]}}}
{
"version": "0.0.0-development",
"commands": {
"create": {
"id": "create",
"description": "create (scaffold) a new app",
"strict": true,
"pluginName": "chimp",
"pluginAlias": "chimp",
"pluginType": "core",
"aliases": [],
"examples": [
"$ chimp create my-new-app",
"$ chimp create my-new-app -a ~src -g ~chimp-helpers"
],
"flags": {
"help": {
"name": "help",
"type": "boolean",
"char": "h",
"description": "Show CLI help.",
"allowNo": false
},
"appPrefix": {
"name": "appPrefix",
"type": "option",
"char": "a",
"description": "prefix that points to the sourcecode of your app",
"multiple": false,
"default": "~app"
},
"generatedPrefix": {
"name": "generatedPrefix",
"type": "option",
"char": "g",
"description": "prefix that points to the generated by chimp helper code",
"multiple": false,
"default": "~generated"
}
},
"args": {
"name": {
"name": "name",
"description": "name of the new app, also used as the directory",
"required": true
}
}
},
"eject": {
"id": "eject",
"description": "eject from chimp",
"strict": true,
"pluginName": "chimp",
"pluginAlias": "chimp",
"pluginType": "core",
"aliases": [],
"examples": [
"$ chimp eject"
],
"flags": {
"help": {
"name": "help",
"type": "boolean",
"char": "h",
"description": "Show CLI help.",
"allowNo": false
}
},
"args": {}
},
"generate": {
"id": "generate",
"description": "generate GraphQL code",
"strict": true,
"pluginName": "chimp",
"pluginAlias": "chimp",
"pluginType": "core",
"aliases": [],
"examples": [
"$ chimp generate",
"$ chimp generate -a ~src -g ~chimp-helpers"
],
"flags": {
"help": {
"name": "help",
"type": "boolean",
"char": "h",
"description": "Show CLI help.",
"allowNo": false
},
"appPrefix": {
"name": "appPrefix",
"type": "option",
"char": "a",
"description": "prefix that points to the sourcecode of your app",
"multiple": false,
"default": "~app"
},
"generatedPrefix": {
"name": "generatedPrefix",
"type": "option",
"char": "g",
"description": "prefix that points to the generated by chimp helper code",
"multiple": false,
"default": "~generated"
},
"modulesPath": {
"name": "modulesPath",
"type": "option",
"char": "p",
"description": "path to the graphQL modules, only use if you are migrating an existing Apollo App and you want to use chimp only for a part of it",
"multiple": false
}
},
"args": {}
},
"init": {
"id": "init",
"description": "init Chimp",
"strict": true,
"pluginName": "chimp",
"pluginAlias": "chimp",
"pluginType": "core",
"aliases": [],
"examples": [
"$ chimp init",
"$ chimp init -p ./src/chimp-modules"
],
"flags": {
"help": {
"name": "help",
"type": "boolean",
"char": "h",
"description": "Show CLI help.",
"allowNo": false
},
"modulesPath": {
"name": "modulesPath",
"type": "option",
"char": "p",
"description": "path to the GraphQL modules.",
"multiple": false,
"default": "./src/modules"
}
},
"args": {}
}
}
}
149 changes: 149 additions & 0 deletions src/commands/eject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import { Command, Flags } from '@oclif/core';
import shell from 'shelljs';
import { execQuietly } from '../generate/helpers/execQuietly';
import { findProjectMainPath } from '../generate/helpers/findProjectMainPath';
import { newTask, setupListr } from '../generate/helpers/ListrHelper';
import { assertGitCleanState } from '../init/assert-git-clean-state';

function installingTooling(projectMainPath: string): Promise<void> {
function determinePackageManager() {
if (fs.existsSync('./yarn.lock')) {
return 'yarn';
}

if (fs.existsSync('./pnpm-lock.yaml')) {
return 'pnpm';
}

return 'npm';
}

function installPackage(packageManager: 'yarn' | 'pnpm' | 'npm', packageName: string) {
let command;

switch (packageManager) {
case 'yarn':
command = `yarn add --dev ${packageName}`;
break;
case 'pnpm':
command = `pnpm add --save-dev ${packageName}`;
break;
case 'npm':
default:
command = `npm install --save-dev ${packageName}`;
break;
}

console.log('installing packages');
return execQuietly(command, { cwd: projectMainPath });
}

const packageManager = determinePackageManager();
return installPackage(packageManager, '@graphql-tools/graphql-file-loader @graphql-tools/load @graphql-tools/merge');
}

function moveCode(projectMainPath: string, chimpMainPath: string) {
shell.cp(
path.join(chimpMainPath, 'src/generate/templates/ejectedSchema.ts'),
path.join(projectMainPath, 'src/schema.ts'),
);

// Move genericDataModelSchema.graphql
shell.mv(
path.join(projectMainPath, 'generated/graphql/genericDataModelSchema.graphql'),
path.join(projectMainPath, 'src/genericDataModelSchema.graphql'),
);

// Move types.ts
shell.mv(path.join(projectMainPath, 'generated/graphql/types.ts'), path.join(projectMainPath, 'src/graphqlTypes.ts'));

for (const resolverFile of shell.ls(path.join(projectMainPath, 'generated/graphql/*Resolvers.ts'))) {
const fileName = resolverFile.split('/').pop();
const moduleName = fileName!.replace('Resolvers.ts', '');
const targetDir = path.join(projectMainPath, `src/modules/${moduleName}/graphql/`);

shell.mv(resolverFile, targetDir);
}

// Move resolvers.ts
shell.mv(
path.join(projectMainPath, 'generated/graphql/resolvers.ts'),
path.join(projectMainPath, 'src/resolvers.ts'),
);

// Move schema.ts
shell.rm(path.join(projectMainPath, 'generated/graphql/schema.ts'));

// Move helpers
for (const helperFile of shell.ls(path.join(projectMainPath, 'generated/graphql/helpers/*.ts'))) {
const fileName = helperFile.split('/').pop()!.replace('SpecWrapper.ts', '');
const importPath = shell.grep(`import { ${fileName} }`, helperFile).replace(/.*from\s+["']([^"']+)["'].*/, '$1');
const resolvedPath = importPath.replace('~app/modules', path.join(projectMainPath, 'src/modules'));
const targetDir = `${resolvedPath.replace(fileName, '').replace('\n', '')}test-helpers`;

shell.mkdir('-p', targetDir);
shell.mv(helperFile, `${targetDir}/${fileName}SpecWrapper.ts`);
}

// Update imports in resolvers.ts
shell.sed(
'-i',
/import { (.+?)Resolvers } from "\.\/(.+?)Resolvers";/g,
'import { $1Resolvers } from "~app/modules/$1/graphql/$1Resolvers";',
path.join(projectMainPath, 'src/resolvers.ts'),
);

// Update imports in ./src/ that use path: "~generated/graphql/types"
shell
.find(path.join(projectMainPath, 'src/'))
.filter((file) => file.match(/\.ts$/))
// eslint-disable-next-line unicorn/no-array-for-each
.forEach((file) => {
shell.sed(
'-i',
`import { schema } from "~generated/graphql/schema"`,
`import { schema } from "~app/schema"`,
file,
);
shell.sed(
'-i',
`import { resolvers } from "~generated/graphql/resolvers"`,
`import { resolvers } from "~app/resolvers"`,
file,
);
shell.sed('-i', `import { Resolvers } from "./types";`, `import { Resolvers } from "~app/graphqlTypes";`, file);
shell.sed('-i', '~generated/graphql/helpers/', './test-helpers/', file);
shell.sed('-i', /~generated\/graphql\/types/g, '~app/graphqlTypes', file);
});
}

export default class Eject extends Command {
static description = 'eject from chimp';

static examples = ['$ chimp eject'];

static flags = {
help: Flags.help({ char: 'h' }),
};

async run() {
await this.parse(Eject);

assertGitCleanState();
const chimpMainPath = path.join(__dirname, '../../');
const projectMainPath = findProjectMainPath();

const tasks = setupListr([
newTask('Moving code', async () => moveCode(projectMainPath, chimpMainPath)),
newTask('Installing GraphQL schema tooling', async () => installingTooling(projectMainPath)),
]);

try {
await tasks.run();
} catch (error) {
console.error(error);
}
}
}
4 changes: 2 additions & 2 deletions src/generate/helpers/execQuietly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import debugConfigurator from 'debug';

const debug = debugConfigurator('execQuietly');

export async function execQuietly(command: string, options: Record<string, unknown>, errorMessage = '') {
export async function execQuietly(command: string, options: Record<string, unknown>, errorMessage = ''): Promise<void> {
return new Promise((resolve, reject) => {
const child = shelljs.exec(command, {
...options,
Expand All @@ -25,7 +25,7 @@ export async function execQuietly(command: string, options: Record<string, unkno
// Listen for the exit event to get the exit code
child.on('exit', (code: number) => {
if (code === 0) {
resolve(stdoutData);
resolve();
} else {
reject(new Error(`${stdoutData} ${errorMessage}: ${stderrData} ${command}`));
}
Expand Down

0 comments on commit e3341e8

Please sign in to comment.