-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): CATALYST-246 create-catalyst login, create env, scaffold p…
…roject
- Loading branch information
1 parent
16f9b05
commit 9264edf
Showing
21 changed files
with
1,381 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
name: CLI | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
pull_request: | ||
types: [opened, synchronize] | ||
merge_group: | ||
types: [checks_requested] | ||
|
||
jobs: | ||
lint-typecheck: | ||
name: Run Tests | ||
|
||
runs-on: ubuntu-latest | ||
|
||
env: | ||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} | ||
TURBO_TEAM: ${{ vars.TURBO_TEAM }} | ||
BIGCOMMERCE_STORE_HASH: ${{ secrets.BIGCOMMERCE_STORE_HASH }} | ||
BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN: ${{ secrets.BIGCOMMERCE_CUSTOMER_IMPERSONATION_TOKEN }} | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@main | ||
with: | ||
fetch-depth: 2 | ||
|
||
- uses: pnpm/action-setup@v2 | ||
|
||
- name: Use Node.js | ||
uses: actions/setup-node@main | ||
with: | ||
node-version-file: ".nvmrc" | ||
cache: "pnpm" | ||
|
||
- name: Install dependencies | ||
run: pnpm install --frozen-lockfile | ||
|
||
- name: Run Tests | ||
run: pnpm test | ||
working-directory: packages/create-catalyst |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/swcrc", | ||
"sourceMaps": true, | ||
"jsc": { | ||
"parser": { | ||
"syntax": "typescript", | ||
"decorators": true, | ||
"dynamicImport": true | ||
}, | ||
"transform": { | ||
"legacyDecorator": true, | ||
"decoratorMetadata": true | ||
}, | ||
"baseUrl": "./" | ||
}, | ||
"minify": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1 @@ | ||
# packages/create-catalyst | ||
|
||
> [!WARNING] | ||
> The create-catalyst package is in development and not published to the NPM registry | ||
Scaffolding for Catalyst storefront projects. | ||
|
||
## Usage | ||
|
||
**NPM:** | ||
|
||
```sh | ||
npm create @bigcommerce/catalyst@latest my-catalyst-store | ||
``` | ||
|
||
**PNPM:** | ||
|
||
```sh | ||
pnpm create @bigcommerce/catalyst@latest my-catalyst-store | ||
``` | ||
|
||
**Yarn:** | ||
|
||
```sh | ||
yarn create @bigcommerce/catalyst@latest my-catalyst-store | ||
``` | ||
|
||
## Contributing | ||
|
||
**Prerequisites:** | ||
|
||
- Node `>=18.16` | ||
- [Verdaccio](https://verdaccio.org/) `>=5` | ||
|
||
**Developing Locally:** | ||
|
||
While developing `create-catalyst` locally, it's essential to test your changes before publishing them to NPM. To achieve this, we utilize Verdaccio, a lightweight private npm proxy registry that you can run in your local environment. By publishing `create-catalyst` to Verdaccio during local development, we can point `[yarn|npm|pnpm] create @bigcommerce/catalyst` at the Verdaccio registry URL and observe how our changes will behave once they are published to NPM. | ||
|
||
1. Install Verdaccio: https://verdaccio.org/docs/installation | ||
2. Run Verdaccio: `verdaccio --listen 4873` | ||
3. Add an NPM user with `@bigcommerce` scope to Verdaccio: `npm adduser --scope=@bigcommerce --registry=http://localhost:4873` | ||
|
||
> ⚠️ **IMPORTANT:** NPM registry data is immutable, meaning once published, a package cannot change. Be careful to ensure that you do not run commands such as `publish` against the default NPM registry if your work is not ready to be published. Always explicitly pass `--registry=http://localhost:<VERDACCIO_PORT>` with commands that modify the registry (such as `publish`) to ensure you only publish to Verdaccio when working locally. | ||
4. If necessary, run `pnpm build` and `pnpm publish --registry=http://localhost:4873` in each Catalyst-scoped package required by relevant examples listed in `apps/` (e.g., Catalyst `core` examples require `@bigcommerce/reactant`, `@bigcommerce/eslint-config-catalyst`, `@bigcommerce/catalyst-configs`, `@bigcommerce/catalyst-client`) | ||
5. Run `pnpm build` and `pnpm publish --registry=http://localhost:4873` in the `@bigcommerce/create-catalyst` package | ||
6. Confirm published packages are listed in Verdaccio: http://localhost:4873 | ||
|
||
In order to point `npm create`, `pnpm create`, and/or `yarn create` to the Verdaccio registry, run one or more of the following commands against the package manager's global configuration: | ||
|
||
- **NPM:** `npm config set @bigcommerce:registry http://localhost:4873` | ||
- **PNPM:** `pnpm config set @bigcommerce:registry http://localhost:4873` | ||
- **Yarn:** `yarn config set npmScopes.bigcommerce.npmRegistryServer "http://localhost:4873" -H` and then `yarn config set unsafeHttpWhitelist "localhost" -H` | ||
|
||
7. Finally, navigate to the directory in which you'd like to create a new Catalyst storefront, and run `[yarn|npm|pnpm] create @bigcommerce/catalyst name-of-your-catalyst-storefront` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module.exports = { | ||
extensionsToTreatAsEsm: ['.ts'], | ||
transform: { | ||
'^.+\\.(t|j)s?$': '@swc/jest', | ||
}, | ||
transformIgnorePatterns: [], | ||
// moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], | ||
moduleNameMapper: { | ||
'^(\\.{1,2}/.*)\\.js$': '$1', | ||
}, | ||
testEnvironment: 'node', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import chalk from 'chalk'; | ||
import { exec as execCallback } from 'child_process'; | ||
import { copySync, outputFileSync, readJsonSync, removeSync, writeJsonSync } from 'fs-extra/esm'; | ||
import merge from 'lodash.merge'; | ||
import set from 'lodash.set'; | ||
import unset from 'lodash.unset'; | ||
import { installDependencies } from 'nypm'; | ||
import { join } from 'path'; | ||
import { promisify } from 'util'; | ||
import * as z from 'zod'; | ||
|
||
import { cloneTemplate } from '../utils/clone.js'; | ||
import { getPackageManager } from '../utils/pm.js'; | ||
import { promptWithValidation } from '../utils/prompt.js'; | ||
import { PkgJsonSchema, TsConfigSchema } from '../utils/schemas.js'; | ||
import { spinner } from '../utils/spinner.js'; | ||
import { validate } from '../utils/validate.js'; | ||
|
||
import { env } from './env.js'; | ||
import { login } from './login.js'; | ||
|
||
const exec = promisify(execCallback); | ||
|
||
export const create = async () => { | ||
const GITHUB_TOKEN = validate( | ||
process.env.GITHUB_TOKEN, | ||
z | ||
.string({ required_error: 'GITHUB_TOKEN is a required environment variable' }) | ||
.trim() | ||
.startsWith('ghp'), | ||
); | ||
|
||
const REF = validate(process.env.REF, z.string().trim().optional().default('main')); | ||
|
||
console.log(chalk.cyanBright('\n◢ @bigcommerce/create-catalyst v0.1.0\n')); | ||
|
||
const { projectName } = await promptWithValidation( | ||
{ | ||
type: 'text', | ||
name: 'projectName', | ||
message: 'What is the name of your project?', | ||
}, | ||
z.object({ projectName: z.string().min(1) }), | ||
); | ||
|
||
const projectDir = join(process.cwd(), projectName); | ||
|
||
const credentials = await login(); | ||
|
||
if (!credentials) { | ||
console.log(chalk.yellow('\n@todo use sample data\n')); | ||
process.exit(0); | ||
} | ||
|
||
const environment = await env({ ...credentials }); | ||
|
||
console.log(`\nCreating '${projectName}' at '${projectDir}'\n`); | ||
|
||
await spinner( | ||
cloneTemplate(`github:bigcommerce/catalyst/apps/core#${REF}`, { | ||
dest: projectDir, | ||
auth: GITHUB_TOKEN, | ||
}), | ||
{ | ||
text: 'Cloning Catalyst template...', | ||
successText: 'Catalyst template cloned successfully', | ||
}, | ||
); | ||
|
||
const componentsTmpDir = join(projectDir, 'tmp', 'ui'); | ||
|
||
await spinner( | ||
cloneTemplate(`github:bigcommerce/catalyst/packages/reactant#${REF}`, { | ||
dest: componentsTmpDir, | ||
auth: GITHUB_TOKEN, | ||
}), | ||
{ | ||
text: 'Cloning Catalyst components...', | ||
successText: 'Catalyst components cloned successfully', | ||
}, | ||
); | ||
|
||
outputFileSync(join(projectDir, '.env.local'), environment); | ||
|
||
copySync(join(componentsTmpDir, 'src/components'), join(projectDir, 'components', 'ui')); | ||
|
||
const paths = { | ||
catalyst: { | ||
tailwind: join(projectDir, 'tailwind.config.js'), | ||
package: join(projectDir, 'package.json'), | ||
tsconfig: join(projectDir, 'tsconfig.json'), | ||
}, | ||
components: { | ||
tailwind: join(componentsTmpDir, 'tailwind.config.js'), | ||
package: join(componentsTmpDir, 'package.json'), | ||
tsconfig: join(componentsTmpDir, 'tsconfig.json'), | ||
}, | ||
}; | ||
|
||
const catalystPkgJson = validate(readJsonSync(paths.catalyst.package), PkgJsonSchema); | ||
const componentsPkgJson = validate(readJsonSync(paths.components.package), PkgJsonSchema); | ||
|
||
const packageJson = merge({}, catalystPkgJson, { | ||
name: projectName, | ||
description: '', | ||
version: '0.1.0', | ||
dependencies: componentsPkgJson.dependencies, | ||
devDependencies: componentsPkgJson.devDependencies, | ||
}); | ||
|
||
unset(packageJson, 'dependencies.@bigcommerce/reactant'); // keep | ||
unset(packageJson, 'devDependencies.react'); // move to dep/devDep | ||
unset(packageJson, 'devDependencies.react-dom'); // move to dep/devDep | ||
|
||
set(packageJson, 'dependencies.@bigcommerce/catalyst-client', '^0.1.0'); // modify pkg.json | ||
set(packageJson, 'devDependencies.@bigcommerce/catalyst-configs', '^0.1.0'); // modify pkg.json | ||
set(packageJson, 'devDependencies.@bigcommerce/eslint-config-catalyst', '^0.1.0'); // modify pkg.json | ||
|
||
writeJsonSync(join(projectDir, 'package.json'), packageJson, { spaces: 2 }); | ||
|
||
const catalystTsConfig = validate(readJsonSync(paths.catalyst.tsconfig), TsConfigSchema); | ||
const componentsTsConfig = validate(readJsonSync(paths.components.tsconfig), TsConfigSchema); | ||
|
||
const tsConfig = merge({}, componentsTsConfig, catalystTsConfig); | ||
|
||
unset(tsConfig, 'compilerOptions.declaration'); | ||
unset(tsConfig, 'compilerOptions.declarationMap'); | ||
|
||
set(tsConfig, 'compilerOptions.paths.@bigcommerce/reactant/*', ['./components/ui/*']); | ||
|
||
writeJsonSync(join(projectDir, 'tsconfig.json'), tsConfig, { spaces: 2 }); | ||
|
||
copySync(paths.components.tailwind, paths.catalyst.tailwind); | ||
|
||
removeSync(join(projectDir, 'tmp')); | ||
|
||
// @todo yarn doesn't work? | ||
const pm = getPackageManager(); | ||
|
||
console.log(`\nUsing ${chalk.bold(pm)}\n`); | ||
|
||
await spinner(installDependencies({ cwd: projectDir, silent: true, packageManager: pm }), { | ||
text: `Installing dependencies. This could take a minute...`, | ||
successText: `Dependencies installed successfully`, | ||
}); | ||
|
||
await spinner(exec(`${pm} run codegen`, { cwd: projectDir }), { | ||
text: 'Generating GraphQL types...', | ||
successText: 'GraphQL types generated successfully', | ||
}); | ||
|
||
await spinner(exec(`${pm} run lint --fix`, { cwd: projectDir }), { | ||
text: 'Linting to validate generated types...', | ||
successText: 'GraphQL types validated successfully', | ||
}); | ||
|
||
console.log(`\n${chalk.green('Success!')} Created '${projectName}' at '${projectDir}'\n`); | ||
|
||
console.log('Next steps:'); | ||
console.log(chalk.yellow(`\ncd ${projectName} && ${pm} run dev\n`)); | ||
}; |
Oops, something went wrong.