polen 0.4.0 → 0.5.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.
@@ -1,70 +1,92 @@
1
1
  import { Command } from '@molt/command';
2
- import { Fs, Manifest, Name, Path, Str } from '@wollybeard/kit';
2
+ import { Err, Fs, Manifest, Name, Path, Str } from '@wollybeard/kit';
3
+ import * as Ansis from 'ansis';
3
4
  import consola from 'consola';
4
5
  import { z } from 'zod';
5
6
  import { $ } from 'zx';
6
7
  const Examples = z.enum([`github`, `pokemon`]);
7
8
  const args = Command
8
9
  .create()
9
- .parameter(`name`, z.string().default(() => Name.generate()).describe(`Defaults to a random name.`))
10
+ .parameter(`name`, z.string().optional().describe(`The name of your project. Used by the package name and the default path. Defaults to a random name.`))
11
+ .parameter(`path`, z.string().optional().describe(`The path to a directory to create your project. Must point to an empty or non-existing directory. Defaults to a new directory named after your project in your cwd (current working directory).`))
10
12
  .parameter(`link`, z.boolean().default(false).describe(`When installing polen, do so as a link. You must have Polen globally linked on your machine.`))
11
13
  .parameter(`example`, Examples.default(`pokemon`).describe(`The example to use to scaffold your project.`))
14
+ .settings({
15
+ parameters: {
16
+ environment: {
17
+ $default: {
18
+ // todo prfix seting doesn't seem to work with Molt!
19
+ prefix: `POLEN_CREATE_`,
20
+ enabled: false,
21
+ },
22
+ },
23
+ },
24
+ })
12
25
  .parse();
13
- const cwd = process.cwd();
14
- const isCwdEmpty = await Fs.isEmptyDir(cwd);
15
- const name = Str.Case.kebab(args.name);
16
- const projectRoot = isCwdEmpty ? cwd : Path.join(cwd, name);
17
- if (!isCwdEmpty) {
18
- const isProjectRootExists = await Fs.exists(projectRoot);
19
- const isEmpty = isProjectRootExists && await Fs.isEmptyDir(projectRoot);
20
- if (isProjectRootExists && !isEmpty) {
21
- consola.error(`Project root of name "${name}" already exists`);
26
+ const getProjectRoot = async () => {
27
+ if (args.path) {
28
+ const stat = await Fs.stat(args.path);
29
+ if (Fs.isNotFoundError(stat))
30
+ return args.path;
31
+ if (stat.isDirectory() && await Fs.isEmptyDir(args.path))
32
+ return args.path;
33
+ consola.error(`The given path ${args.path} already exists and is not an empty directory`);
22
34
  process.exit(1);
23
35
  }
24
- consola.info(`creating your project in ./${name}`);
25
- }
26
- else {
27
- consola.info(`Current working directory is empty, creating your project here`);
28
- }
29
- // Scaffold Example
30
- // Pull an example from the Polen repository
31
- // Copy all the files to the project root
32
- const repository = `https://github.com/the-guild-org/polen`;
33
- const repoExampleDir = `examples/${args.example}`;
34
- consola.info(`Scaffolding "${args.example}" example...`);
35
- try {
36
- // Create temporary directory
37
- const { stdout: tmpDir } = await $ `mktemp -d`;
38
- const tmpDirPath = tmpDir.trim();
36
+ const findUsableName = async (isRetry) => {
37
+ const projectRoot = Path.join(process.cwd(), name + (isRetry ? `-${Date.now().toString(36).substring(2, 8)}` : ``));
38
+ const stat = await Fs.stat(projectRoot);
39
+ if (Fs.isNotFoundError(stat))
40
+ return projectRoot;
41
+ if (stat.isDirectory() && await Fs.isEmptyDir(projectRoot))
42
+ return projectRoot;
43
+ return await findUsableName(true);
44
+ };
45
+ return await findUsableName();
46
+ };
47
+ const copyGitRepositoryTemplate = async (input) => {
39
48
  try {
40
- // Clone only the specific example directory using sparse checkout
41
- await $({ quiet: true }) `git clone --depth 1 --filter=blob:none --sparse ${repository} ${tmpDirPath}`;
42
- await $ `cd ${tmpDirPath} && git sparse-checkout set ${repoExampleDir}`;
43
- const sourceExamplePath = Path.join(tmpDirPath, repoExampleDir);
44
- // Verify the example exists
45
- if (!await Fs.exists(sourceExamplePath)) {
46
- throw new Error(`Example "${args.example}" not found in the repository`);
47
- }
48
- // Create project root directory if needed
49
- await $ `mkdir -p ${projectRoot}`;
50
- // Copy files excluding build artifacts and dependencies using rsync
51
- await $ `rsync -av --exclude="node_modules/" --exclude="dist/" --exclude=".bundle-explorer/" --exclude="pnpm-lock.yaml" ${sourceExamplePath}/ ${projectRoot}/`;
52
- consola.success(`Done`);
53
- if (!isCwdEmpty) {
49
+ const tmpDirPath = await Fs.makeTemporaryDirectory();
50
+ const cleanup = async () => await $ `rm -rf ${tmpDirPath}`;
51
+ try {
52
+ // Clone only the specific example directory using sparse checkout
53
+ await $({
54
+ quiet: true,
55
+ }) `git clone --depth 1 --filter=blob:none --sparse ${input.repository.url.href} ${tmpDirPath}`;
56
+ await $ `cd ${tmpDirPath} && git sparse-checkout set ${input.repository.templatePath}`;
57
+ const templatePath = Path.join(tmpDirPath, input.repository.templatePath);
58
+ // Verify the example exists
59
+ if (!await Fs.exists(templatePath)) {
60
+ consola.error(`Example "${args.example}" not found in the Polen repository. Are you using the latest version of the CLI?`);
61
+ await cleanup();
62
+ process.exit(1);
63
+ }
64
+ await $ `mkdir -p ${input.destinationPath}`;
65
+ await $ `rsync -av ${templatePath}/ ${projectRoot}/`;
54
66
  consola.success(`Your project is ready! Get Started:`);
55
67
  }
68
+ finally {
69
+ await cleanup();
70
+ }
56
71
  }
57
- finally {
58
- // Clean up temporary directory
59
- await $ `rm -rf ${tmpDirPath}`;
72
+ catch (error) {
73
+ consola.error(`Failed to scaffold example`);
74
+ Err.log(Err.ensure(error));
75
+ process.exit(1);
60
76
  }
61
- }
62
- catch (error) {
63
- consola.error(`Failed to scaffold example: ${error instanceof Error ? error.message : String(error)}`);
64
- process.exit(1);
65
- }
66
- // End Scaffold Example
67
- // --------------------
77
+ };
78
+ const name = Str.Case.kebab(args.name ?? Name.generate());
79
+ const projectRoot = await getProjectRoot();
80
+ console.log(``);
81
+ consola.info(`Creating your Polen project "${Ansis.greenBright(Str.Case.title(name))}"`);
82
+ consola.info(`Initializing with example "${Ansis.green(Str.Case.title(args.example))}"`);
83
+ await copyGitRepositoryTemplate({
84
+ repository: {
85
+ url: new URL(`https://github.com/the-guild-org/polen`),
86
+ templatePath: Path.join(`examples`, args.example),
87
+ },
88
+ destinationPath: projectRoot,
89
+ });
68
90
  await Manifest.resource.update((manifest) => {
69
91
  manifest.name = name;
70
92
  manifest.description = `A new project`;
@@ -74,5 +96,5 @@ if (args.link) {
74
96
  await $ `pnpm link polen`;
75
97
  }
76
98
  console.log(``);
77
- console.log(` cd ./${Path.relative(cwd, projectRoot)} && pnpm dev`);
99
+ console.log(Ansis.magenta(` cd ${Path.relative(process.cwd(), projectRoot)} && pnpm dev`));
78
100
  //# sourceMappingURL=create.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/cli/commands/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/cli/commands/create.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polen",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "A framework for delightful GraphQL developer portals",
6
6
  "author": {
@@ -60,7 +60,7 @@
60
60
  "@swc/core": "^1.11.29",
61
61
  "@types/jsesc": "^3.0.3",
62
62
  "@vitejs/plugin-react": "^4.4.1",
63
- "@wollybeard/kit": "^0.25.0",
63
+ "@wollybeard/kit": "^0.26.1",
64
64
  "@wollybeard/projector": "^0.1.0",
65
65
  "ansis": "^4.0.0",
66
66
  "consola": "^3.4.2",
@@ -1,5 +1,6 @@
1
1
  import { Command } from '@molt/command'
2
- import { Fs, Manifest, Name, Path, Str } from '@wollybeard/kit'
2
+ import { Err, Fs, Manifest, Name, Path, Str } from '@wollybeard/kit'
3
+ import * as Ansis from 'ansis'
3
4
  import consola from 'consola'
4
5
  import { z } from 'zod'
5
6
  import { $ } from 'zx'
@@ -8,7 +9,18 @@ const Examples = z.enum([`github`, `pokemon`])
8
9
 
9
10
  const args = Command
10
11
  .create()
11
- .parameter(`name`, z.string().default(() => Name.generate()).describe(`Defaults to a random name.`))
12
+ .parameter(
13
+ `name`,
14
+ z.string().optional().describe(
15
+ `The name of your project. Used by the package name and the default path. Defaults to a random name.`,
16
+ ),
17
+ )
18
+ .parameter(
19
+ `path`,
20
+ z.string().optional().describe(
21
+ `The path to a directory to create your project. Must point to an empty or non-existing directory. Defaults to a new directory named after your project in your cwd (current working directory).`,
22
+ ),
23
+ )
12
24
  .parameter(
13
25
  `link`,
14
26
  z.boolean().default(false).describe(
@@ -16,73 +28,96 @@ const args = Command
16
28
  ),
17
29
  )
18
30
  .parameter(`example`, Examples.default(`pokemon`).describe(`The example to use to scaffold your project.`))
31
+ .settings({
32
+ parameters: {
33
+ environment: {
34
+ $default: {
35
+ // todo prfix seting doesn't seem to work with Molt!
36
+ prefix: `POLEN_CREATE_`,
37
+ enabled: false,
38
+ },
39
+ },
40
+ },
41
+ })
19
42
  .parse()
20
43
 
21
- const cwd = process.cwd()
22
- const isCwdEmpty = await Fs.isEmptyDir(cwd)
23
- const name = Str.Case.kebab(args.name)
24
- const projectRoot = isCwdEmpty ? cwd : Path.join(cwd, name)
25
-
26
- if (!isCwdEmpty) {
27
- const isProjectRootExists = await Fs.exists(projectRoot)
28
- const isEmpty = isProjectRootExists && await Fs.isEmptyDir(projectRoot)
29
- if (isProjectRootExists && !isEmpty) {
30
- consola.error(`Project root of name "${name}" already exists`)
44
+ const getProjectRoot = async (): Promise<string> => {
45
+ if (args.path) {
46
+ const stat = await Fs.stat(args.path)
47
+ if (Fs.isNotFoundError(stat)) return args.path
48
+ if (stat.isDirectory() && await Fs.isEmptyDir(args.path)) return args.path
49
+ consola.error(`The given path ${args.path} already exists and is not an empty directory`)
31
50
  process.exit(1)
32
51
  }
33
- consola.info(`creating your project in ./${name}`)
34
- } else {
35
- consola.info(`Current working directory is empty, creating your project here`)
36
- }
37
-
38
- // Scaffold Example
39
52
 
40
- // Pull an example from the Polen repository
41
- // Copy all the files to the project root
42
- const repository = `https://github.com/the-guild-org/polen`
43
- const repoExampleDir = `examples/${args.example}`
44
-
45
- consola.info(`Scaffolding "${args.example}" example...`)
53
+ const findUsableName = async (isRetry?: true): Promise<string> => {
54
+ const projectRoot = Path.join(process.cwd(), name + (isRetry ? `-${Date.now().toString(36).substring(2, 8)}` : ``))
55
+ const stat = await Fs.stat(projectRoot)
56
+ if (Fs.isNotFoundError(stat)) return projectRoot
57
+ if (stat.isDirectory() && await Fs.isEmptyDir(projectRoot)) return projectRoot
58
+ return await findUsableName(true)
59
+ }
46
60
 
47
- try {
48
- // Create temporary directory
49
- const { stdout: tmpDir } = await $`mktemp -d`
50
- const tmpDirPath = tmpDir.trim()
61
+ return await findUsableName()
62
+ }
51
63
 
64
+ const copyGitRepositoryTemplate = async (input: {
65
+ repository: {
66
+ url: URL
67
+ templatePath: string
68
+ }
69
+ destinationPath: string
70
+ }): Promise<void> => {
52
71
  try {
53
- // Clone only the specific example directory using sparse checkout
54
- await $({ quiet: true })`git clone --depth 1 --filter=blob:none --sparse ${repository} ${tmpDirPath}`
55
- await $`cd ${tmpDirPath} && git sparse-checkout set ${repoExampleDir}`
56
-
57
- const sourceExamplePath = Path.join(tmpDirPath, repoExampleDir)
58
-
59
- // Verify the example exists
60
- if (!await Fs.exists(sourceExamplePath)) {
61
- throw new Error(`Example "${args.example}" not found in the repository`)
62
- }
63
-
64
- // Create project root directory if needed
65
- await $`mkdir -p ${projectRoot}`
66
-
67
- // Copy files excluding build artifacts and dependencies using rsync
68
- await $`rsync -av --exclude="node_modules/" --exclude="dist/" --exclude=".bundle-explorer/" --exclude="pnpm-lock.yaml" ${sourceExamplePath}/ ${projectRoot}/`
72
+ const tmpDirPath = await Fs.makeTemporaryDirectory()
73
+ const cleanup = async () => await $`rm -rf ${tmpDirPath}`
74
+
75
+ try {
76
+ // Clone only the specific example directory using sparse checkout
77
+ await $({
78
+ quiet: true,
79
+ })`git clone --depth 1 --filter=blob:none --sparse ${input.repository.url.href} ${tmpDirPath}`
80
+ await $`cd ${tmpDirPath} && git sparse-checkout set ${input.repository.templatePath}`
81
+
82
+ const templatePath = Path.join(tmpDirPath, input.repository.templatePath)
83
+
84
+ // Verify the example exists
85
+ if (!await Fs.exists(templatePath)) {
86
+ consola.error(
87
+ `Example "${args.example}" not found in the Polen repository. Are you using the latest version of the CLI?`,
88
+ )
89
+ await cleanup()
90
+ process.exit(1)
91
+ }
92
+
93
+ await $`mkdir -p ${input.destinationPath}`
94
+ await $`rsync -av ${templatePath}/ ${projectRoot}/`
69
95
 
70
- consola.success(`Done`)
71
-
72
- if (!isCwdEmpty) {
73
96
  consola.success(`Your project is ready! Get Started:`)
97
+ } finally {
98
+ await cleanup()
74
99
  }
75
- } finally {
76
- // Clean up temporary directory
77
- await $`rm -rf ${tmpDirPath}`
100
+ } catch (error) {
101
+ consola.error(`Failed to scaffold example`)
102
+ Err.log(Err.ensure(error))
103
+ process.exit(1)
78
104
  }
79
- } catch (error) {
80
- consola.error(`Failed to scaffold example: ${error instanceof Error ? error.message : String(error)}`)
81
- process.exit(1)
82
105
  }
83
106
 
84
- // End Scaffold Example
85
- // --------------------
107
+ const name = Str.Case.kebab(args.name ?? Name.generate())
108
+ const projectRoot = await getProjectRoot()
109
+
110
+ console.log(``)
111
+ consola.info(`Creating your Polen project "${Ansis.greenBright(Str.Case.title(name))}"`)
112
+
113
+ consola.info(`Initializing with example "${Ansis.green(Str.Case.title(args.example))}"`)
114
+ await copyGitRepositoryTemplate({
115
+ repository: {
116
+ url: new URL(`https://github.com/the-guild-org/polen`),
117
+ templatePath: Path.join(`examples`, args.example),
118
+ },
119
+ destinationPath: projectRoot,
120
+ })
86
121
 
87
122
  await Manifest.resource.update((manifest) => {
88
123
  manifest.name = name
@@ -95,4 +130,4 @@ if (args.link) {
95
130
  }
96
131
 
97
132
  console.log(``)
98
- console.log(` cd ./${Path.relative(cwd, projectRoot)} && pnpm dev`)
133
+ console.log(Ansis.magenta(` cd ${Path.relative(process.cwd(), projectRoot)} && pnpm dev`))