Skip to content

Commit

Permalink
Set up bootstrap script
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Jul 25, 2024
1 parent 7a9c560 commit 337c23a
Show file tree
Hide file tree
Showing 65 changed files with 361 additions and 39 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ node_modules

*.local

packages/*/build

sandbox/*
!sandbox/.gitkeep

Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.0.0",
"packages": ["packages/*", "templates/*"],
"packages": ["packages/*"],
"npmClient": "yarn"
}
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@
"private": true,
"packageManager": "[email protected]",
"workspaces": [
"packages/*",
"templates/*"
"packages/*"
],
"scripts": {
"dev": "lerna run dev",
"build": "lerna run build",
"lint": "lerna run lint",
"preview": "lerna run preview"
"preview": "lerna run preview",
"clean": "lerna run clean"
},
"devDependencies": {
"@types/meow": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"eslint": "^8.57.0",
"lerna": "^8.1.6",
"meow": "^13.2.0",
"prettier": "^3.3.3",
"typescript": "^5.2.2",
"vite": "^5.3.4"
},
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 0 additions & 1 deletion packages/create-common/bin.js

This file was deleted.

9 changes: 6 additions & 3 deletions packages/create-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "@carto/create-common",
"packageManager": "[email protected]",
"author": "Don McCurdy <[email protected]>",
"version": "0.0.0",
"license": "MIT",
"publishConfig": {
"access": "public",
Expand All @@ -21,9 +22,9 @@
}
},
"browserslist": [
"defaults",
"not IE 11",
"node >= 18"
"defaults",
"not IE 11",
"node >= 18"
],
"scripts": {
"build": "microbundle --format cjs,modern --no-compress --define VERSION=$npm_package_version",
Expand All @@ -36,9 +37,11 @@
},
"dependencies": {
"kolorist": "^1.8.0",
"meow": "^13.2.0",
"prompts": "^2.4.2"
},
"devDependencies": {
"@types/meow": "^6.0.0",
"@types/prompts": "^2.4.9",
"microbundle": "^0.15.1"
}
Expand Down
294 changes: 271 additions & 23 deletions packages/create-common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,271 @@
import prompts from 'prompts';

const config = await prompts([
{
name: 'title',
type: 'text',
message: 'Title for the application',
validate: (text) => text.length === 0 ? 'Title is required' : true
},
{
name: 'accessToken',
type: 'text',
message: 'Access token for CARTO API',
validate: (text) => text.length === 0 ? 'Access token is required' : true
},
{
name: 'apiBaseUrl',
type: 'text',
message: 'Base URL for CARTO API (optional)',
}
])

console.log(config)
import {
copyFileSync,
existsSync,
mkdirSync,
readdirSync,
rmSync,
statSync,
} from "node:fs";
import {
rm,
stat,
copyFile,
readFile,
writeFile,
mkdir,
} from "node:fs/promises";
import { resolve } from "node:path";
import prompts from "prompts";
import { green, bold, dim, bgGray, bgGreen, yellow } from "kolorist";

// TODO: Simplify this.
const pkg = pkgFromUserAgent(process.env.npm_config_user_agent);
const pkgManager = pkg ? pkg.name : "npm";
const isYarn1 = pkgManager === "yarn" && pkg?.version.startsWith("1.");

/** List of relative paths in the template to be _removed_ from new projects. */
const TEMPLATE_EXCLUDE_PATHS = ["node_modules", "scripts"];

/** List of dependencies in the template to be _removed_ from new projects. */
const TEMPLATE_EXCLUDE_DEPS = ["@carto/create-common"];

/**
* List of package.json fields to clear from new projects.
* See: https://docs.npmjs.com/cli/v10/configuring-npm/package-json
*/
const TEMPLATE_EXCLUDE_PKG_FIELDS = [
"author",
"bin",
"bugs",
"description",
"files",
"homepage",
"keywords",
"license",
"publishConfig",
"repository",
"version",
];

/**
* Creates a new CARTO app in the target directory, given a template.
*
* @param templateDir Absolute path to template directory
* @param targetDir Relative or absolute path to target directory
*/
export async function createProject(
templateDir: string,
inputTargetDir: string,
) {
const targetDir = resolve(process.cwd(), inputTargetDir);

console.log(`
${green("✔")} ${bold("Template directory")} ${dim("…")} ${templateDir}
${green("✔")} ${bold("Target directory")} ${dim("…")} ${targetDir}
`);

console.log(dim("…\n"));

/****************************************************************************
* Validate target directory.
*/

if (targetDir === templateDir) {
throw new Error(`Target and template directories cannot be the same.`);
}

if (existsSync(targetDir) && !isEmpty(targetDir)) {
const { overwrite } = await prompts([
{
type: "confirm",
name: "overwrite",
message: `Target directory "${targetDir}" is not empty. Overwrite?`,
},
]);

if (!overwrite) {
console.warn(`Project creation cancelled.`);
process.exit(2);
}
} else if (!existsSync(targetDir)) {
mkdirSync(targetDir, { recursive: true });
}

/****************************************************************************
* Project configuration.
*/

const config = await prompts(
[
{
name: "title",
type: "text",
message: "Title for the application",
validate: (text) => (text.length === 0 ? "Title is required" : true),
},
{
name: "accessToken",
type: "password",
message: "Access token for CARTO API",
validate: (text) =>
text.length === 0 ? "Access token is required" : true,
},
{
name: "apiBaseUrl",
type: "text",
message: "Base URL for CARTO API (optional)",
},
],
{
onCancel: () => {
console.warn(`Project creation cancelled.`);
process.exit(2);
},
},
);

console.log(dim("\n…"));

/****************************************************************************
* Populate project directory.
*/

// Overwrite was explicitly approved by user above.
if (existsSync(targetDir) && !isEmpty(targetDir)) {
emptyDir(targetDir);
}

copyDir(templateDir, targetDir);

// Remove template files not needed in project.
for (const excludePath of TEMPLATE_EXCLUDE_PATHS) {
await rm(resolve(targetDir, excludePath), { recursive: true, force: true });
}

// Set up package.json.
const pkgPath = resolve(targetDir, "package.json");
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
removePkgDependencies(pkg, TEMPLATE_EXCLUDE_DEPS);
removePkgFields(pkg, TEMPLATE_EXCLUDE_PKG_FIELDS);
pkg.name = toValidPackageName(config.title);
pkg.private = true;
await writeFile(pkgPath, JSON.stringify(pkg, null, 2));

// Suggest next steps
console.log(`
${green("✔")} ${bold(`Project "${config.title}" was created!`)}
${bold(yellow("!"))} ${bold("Next steps")}:
${[
...(inputTargetDir !== "." ? [`${dim("$")} cd ${inputTargetDir}`] : []),
`${dim("$")} yarn`,
`${dim("$")} yarn dev`,
].join("\n")}
`);
}

/******************************************************************************
* Utility functions.
*
* References:
* - https://github.com/vitejs/vite/blob/main/packages/create-vite/src/index.ts
*/

function removePkgDependencies<T extends Record<string, unknown>>(
pkg: T,
excludeDeps: string[],
): T {
const dependencyTypes = [
"dependencies",
"devDependencies",
"optionalDependencies",
"peerDependencies",
];

for (const exclude of excludeDeps) {
for (const type of dependencyTypes) {
const dependencies = pkg[type] as Record<string, string> | undefined;
if (dependencies && dependencies[exclude]) {
delete dependencies[exclude];
}
}
}

return pkg;
}

function removePkgFields<T extends Record<string, unknown>>(
pkg: T,
excludeFields: string[],
): T {
for (const field of TEMPLATE_EXCLUDE_PKG_FIELDS) {
delete pkg[field];
}
return pkg;
}

function formatTargetDir(targetDir: string | undefined) {
return targetDir?.trim().replace(/\/+$/g, "");
}

function copy(src: string, dest: string) {
const stat = statSync(src);
if (stat.isDirectory()) {
copyDir(src, dest);
} else {
copyFileSync(src, dest);
}
}

const VALIDATE_PKG_NAME_REGEX =
/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/;

function toValidPackageName(projectName: string) {
if (VALIDATE_PKG_NAME_REGEX.test(projectName)) {
return projectName;
}

return projectName
.trim()
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/^[._]/, "")
.replace(/[^a-z\d\-~]+/g, "-");
}

function copyDir(srcDir: string, destDir: string) {
mkdirSync(destDir, { recursive: true });
for (const file of readdirSync(srcDir)) {
const srcFile = resolve(srcDir, file);
const destFile = resolve(destDir, file);
copy(srcFile, destFile);
}
}

function isEmpty(path: string) {
const files = readdirSync(path);
return files.length === 0 || (files.length === 1 && files[0] === ".git");
}

function emptyDir(dir: string) {
if (!existsSync(dir)) {
return;
}
for (const file of readdirSync(dir)) {
if (file === ".git") {
continue;
}
rmSync(resolve(dir, file), { recursive: true, force: true });
}
}

function pkgFromUserAgent(userAgent: string | undefined) {
if (!userAgent) return undefined;
const pkgSpec = userAgent.split(" ")[0];
const pkgSpecArr = pkgSpec.split("/");
return {
name: pkgSpecArr[0],
version: pkgSpecArr[1],
};
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"version": "0.0.0",
"type": "module",
"bin": "scripts/create.js",
"scripts": {
"dev": "vite --port 4001",
"build": "tsc -b && vite build",
Expand All @@ -18,6 +19,7 @@
},
"dependencies": {
"@carto/api-client": "^0.0.1-2",
"@carto/create-common": "^0.0.0",
"@deck.gl/aggregation-layers": "^9.0.24",
"@deck.gl/carto": "^9.0.24",
"@deck.gl/core": "^9.0.24",
Expand Down
File renamed without changes
Loading

0 comments on commit 337c23a

Please sign in to comment.