Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Developing external extensions (v0.1) #57

Merged
merged 17 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions extensions/eip/extension/package.json
carletex marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"scripts": {
"eip": "echo 'yooooo'"
}
}
7 changes: 7 additions & 0 deletions extensions/eip/extension/packages/nextjs/app/eip/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextPage } from "next";

const Eip: NextPage = () => {
return <div className="flex items-center flex-col flex-grow pt-10 px-8">Testtttttt!</div>;

Check failure on line 4 in extensions/eip/extension/packages/nextjs/app/eip/page.tsx

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, lts/*)

Unsafe return of an `any` typed value
};

export default Eip;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const menuIconImports = `import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";`;
export const menuObjects = `{
label: "EIP!",
href: "/eip",
icon: <MagnifyingGlassIcon className="h-4 w-4" />,
}`;
45 changes: 29 additions & 16 deletions src/tasks/copy-template-files.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { execa } from "execa";
import { Options, TemplateDescriptor } from "../types";
import { ExternalExtension, Options, TemplateDescriptor } from "../types";
import { baseDir } from "../utils/consts";
import { extensionDict } from "../utils/extensions-dictionary";
import { findFilesRecursiveSync } from "../utils/find-files-recursively";
Expand Down Expand Up @@ -130,15 +130,18 @@ const processTemplatedFiles = async (
)
.flat();

const externalExtensionFolder = isDev
? path.join(basePath, "../../extensions", externalExtension as string, "extension")
: path.join(targetDir, EXTERNAL_EXTENSION_TMP_FOLDER, "extension");
const externalExtensionTemplatedFileDescriptors: TemplateDescriptor[] = externalExtension
? findFilesRecursiveSync(path.join(targetDir, EXTERNAL_EXTENSION_TMP_FOLDER, "extension"), filePath =>
isTemplateRegex.test(filePath),
).map(extensionTemplatePath => ({
path: extensionTemplatePath,
fileUrl: url.pathToFileURL(extensionTemplatePath).href,
relativePath: extensionTemplatePath.split(path.join(targetDir, EXTERNAL_EXTENSION_TMP_FOLDER, "extension"))[1],
source: `external extension ${getArgumentFromExternalExtensionOption(externalExtension)}`,
}))
? findFilesRecursiveSync(externalExtensionFolder, filePath => isTemplateRegex.test(filePath)).map(
extensionTemplatePath => ({
path: extensionTemplatePath,
fileUrl: url.pathToFileURL(extensionTemplatePath).href,
relativePath: extensionTemplatePath.split(externalExtensionFolder)[1],
source: `external extension ${isDev ? (externalExtension as string) : getArgumentFromExternalExtensionOption(externalExtension)}`,
}),
)
: [];

await Promise.all(
Expand All @@ -163,7 +166,9 @@ const processTemplatedFiles = async (
.flat();

if (externalExtension) {
const argsFilePath = path.join(targetDir, EXTERNAL_EXTENSION_TMP_FOLDER, "extension", argsPath);
const argsFilePath = isDev
? path.join(basePath, "../../extensions", externalExtension as string, "extension", argsPath)
: path.join(targetDir, EXTERNAL_EXTENSION_TMP_FOLDER, "extension", argsPath);

const fileExists = fs.existsSync(argsFilePath);
if (fileExists) {
Expand Down Expand Up @@ -225,7 +230,7 @@ templates/${templateFileDescriptor.source}${templateFileDescriptor.relativePath}
--- ARGS FILES
${
hasArgsPaths
? argsFileUrls.map(url => `\t- ${path.join("templates", url.split("templates")[1])}`).join("\n")
? argsFileUrls.map(url => `\t- ${url.split("templates")[1] || url.split("extensions")[1]}`).join("\n")
: "(no args files writing to the template)"
}

Expand All @@ -250,8 +255,10 @@ const setUpExternalExtensionFiles = async (options: Options, tmpDir: string) =>
// 1. Create tmp directory to clone external extension
await fs.promises.mkdir(tmpDir);

const repository = options.externalExtension!.repository;
const branch = options.externalExtension!.branch;
// dev mode, the external extension should be in the "extensions" folder already
if (options.dev) return;

const { repository, branch } = options.externalExtension as ExternalExtension;

// 2. Clone external extension
if (branch) {
Expand Down Expand Up @@ -281,15 +288,21 @@ export async function copyTemplateFiles(options: Options, templateDir: string, t

// 3. Set up external extension if needed
if (options.externalExtension) {
await setUpExternalExtensionFiles(options, tmpDir);
await copyExtensionsFiles(options, path.join(tmpDir, "extension"), targetDir);
let externalExtensionPath = path.join(tmpDir, "extension");
if (options.dev) {
externalExtensionPath = path.join(templateDir, "../extensions", options.externalExtension as string, "extension");
} else {
await setUpExternalExtensionFiles(options, tmpDir);
}

await copyExtensionsFiles(options, externalExtensionPath, targetDir);
}

// 4. Process templated files and generate output
await processTemplatedFiles(options, basePath, targetDir);

// 5. Delete tmp directory
if (options.externalExtension) {
if (options.externalExtension && !options.dev) {
await fs.promises.rm(tmpDir, { recursive: true });
}

Expand Down
4 changes: 3 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ export type RawOptions = {
install: boolean | null;
dev: boolean;
extensions: Extension[] | null;
externalExtension: ExternalExtension | null;
externalExtension: ExternalExtension | ExternalExtensionNameDev | null;
};

export type ExternalExtension = {
repository: string;
branch?: string | null;
};

export type ExternalExtensionNameDev = string;

type NonNullableRawOptions = {
[Prop in keyof Omit<RawOptions, "externalExtension">]: NonNullable<RawOptions[Prop]>;
} & {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/external-extensions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RawOptions } from "../types";
import { ExternalExtension, RawOptions } from "../types";
import { CURATED_EXTENSIONS } from "../config";

// Gets the data from the argument passed to the `--extension` option.
Expand Down Expand Up @@ -37,7 +37,7 @@ export const getDataFromExternalExtensionArgument = (externalExtension: string)
// Parse the externalExtensionOption object into a argument string.
// e.g. { repository: "owner/project", branch: "branch" } => "owner/project:branch"
export const getArgumentFromExternalExtensionOption = (externalExtensionOption: RawOptions["externalExtension"]) => {
const { repository, branch } = externalExtensionOption || {};
const { repository, branch } = (externalExtensionOption as ExternalExtension) || {};

const owner = repository?.split("/")[3];
const project = repository?.split("/")[4];
Expand Down
32 changes: 27 additions & 5 deletions src/utils/parse-arguments-into-options.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import type { Args, RawOptions } from "../types";
import type { Args, ExternalExtension, RawOptions } from "../types";
import arg from "arg";
import * as https from "https";
import { getDataFromExternalExtensionArgument } from "./external-extensions";
import chalk from "chalk";
import { CURATED_EXTENSIONS } from "../config";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const validateTemplate = async (
template: string,
dev: boolean,
): Promise<{ repository: string; branch?: string } | string> => {
// if dev, chec that the template folders exists in extensions/${template}
if (dev) {
carletex marked this conversation as resolved.
Show resolved Hide resolved
try {
// The directory exists
const currentFileUrl = import.meta.url;
const extensionsDirectory = path.resolve(decodeURI(fileURLToPath(currentFileUrl)), "../../extensions");
console.log("extensionsDirectory", extensionsDirectory);
await fs.promises.access(`${extensionsDirectory}/${template}`);
} catch {
// The directory does not exist
throw new Error(`Template not found in "extensions/${template}"`);
}

return template;
}

const validateTemplate = async (template: string): Promise<{ repository: string; branch?: string }> => {
const { githubUrl, githubBranchUrl, branch } = getDataFromExternalExtensionArgument(template);

// Check if repository exists
Expand Down Expand Up @@ -57,13 +79,13 @@ export async function parseArgumentsIntoOptions(rawArgs: Args): Promise<RawOptio

// ToDo. Allow multiple
// ToDo. Allow core extensions too
const extension = args["--extension"] ? await validateTemplate(args["--extension"]) : null;
const extension = args["--extension"] ? await validateTemplate(args["--extension"], dev) : null;

if (extension && !CURATED_EXTENSIONS[args["--extension"] as string]) {
if (!dev && extension && !CURATED_EXTENSIONS[args["--extension"] as string]) {
console.log(
chalk.yellow(
` You are using a third-party extension. Make sure you trust the source of ${chalk.yellow.bold(
extension.repository,
(extension as ExternalExtension).repository,
)}\n`,
),
);
Expand Down
Loading