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

feat: Provide init command #2

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
dist
.DS_Store

18 changes: 13 additions & 5 deletions src/app/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { createCommand } from "commander";
import { LiffApiClient } from "../../api/liff.js";
import { resolveChannel } from "../../channel/resolveChannel.js";

const createAction = async (options: {
export type CreateAppOptions = {
channelId?: string;
name: string;
endpointUrl: string;
viewType: string;
}) => {
};

export const createLiffApp = async (options: CreateAppOptions) => {
const accessToken = (await resolveChannel(options?.channelId))?.accessToken;
if (!accessToken) {
throw new Error(`Access token not found.
Expand All @@ -27,6 +29,12 @@ const createAction = async (options: {
description: options.name,
});

return liffId;
};

const createAction = async (options: CreateAppOptions) => {
const liffId = await createLiffApp(options);

console.info(`Successfully created LIFF app: ${liffId}`);
};

Expand All @@ -36,16 +44,16 @@ export const makeCreateCommand = () => {
.description("Create a new LIFF app")
.option(
"-c, --channel-id [channelId]",
"The channel ID to use. If it isn't specified, the currentChannelId's will be used.",
"The channel ID to use. If it isn't specified, the currentChannelId will be used.",
)
.requiredOption("-n, --name <name>", "The name of the LIFF app")
.requiredOption(
"-e, --endpoint-url <endpointUrl>",
"The endpoint URL of the LIFF app",
"The endpoint URL of the LIFF app. Must be 'https://'",
)
.requiredOption(
"-v, --view-type <viewType>",
"The view type of the LIFF app",
"The view type of the LIFF app. Must be 'compact', 'tall', or 'full'",
)
.action(createAction);

Expand Down
2 changes: 1 addition & 1 deletion src/app/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const makeUpdateCommand = () => {
.requiredOption("-l, --liff-id <liffId>", "The LIFF ID to update")
.option(
"-c, --channel-id [channelId]",
"The channel ID to use. If it isn't specified, the currentChannelId's will be used.",
"The channel ID to use. If it isn't specified, the currentChannelId will be used.",
)
.option("-n, --name [name]", "The name of the LIFF app")
.option(
Expand Down
4 changes: 3 additions & 1 deletion src/channel/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import inquirer from "inquirer";

import { renewAccessToken } from "../renewAccessToken.js";

const addAction: (channelId?: string) => Promise<void> = async (channelId) => {
export const addAction: (channelId?: string) => Promise<void> = async (
channelId,
) => {
if (!channelId) {
throw new Error("Channel ID is required.");
}
Expand Down
102 changes: 102 additions & 0 deletions src/init/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Command } from "commander";
import { addAction as addChannelAction } from "../channel/commands/add.js";
import { execSync } from "child_process";
import { CreateAppOptions, createLiffApp } from "../app/commands/create.js";
import inquirer from "inquirer";

const DEFAULT_ENDPOINT_URL = "https://localhost:9000";

const addAction: (options: CreateAppOptions) => Promise<void> = async (
options,
) => {
// collect required information via prompt if not specified via parameter
const promptItems = [];

if (!options.channelId) {
promptItems.push({
type: "input",
name: "channelId",
message: "Channel ID?",
});
}

if (!options.name) {
promptItems.push({
type: "input",
name: "name",
message: "App name?",
});
}

if (!options.viewType) {
promptItems.push({
type: "list",
name: "viewType",
message: "View type?",
choices: ["compact", "tall", "full"],
});
}

if (!options.endpointUrl) {
promptItems.push({
type: "input",
name: "endpointUrl",
message: `Endpoint URL? (leave empty for default '${DEFAULT_ENDPOINT_URL}')`,
});
}

const promptInputs = await inquirer.prompt<{ [key: string]: string }>(
promptItems,
);

options.channelId = promptInputs.channelId ?? options.channelId;
options.name = promptInputs.name ?? options.name;
options.viewType = promptInputs.viewType ?? options.viewType;
options.endpointUrl =
promptInputs.endpointUrl?.length > 0
? promptInputs.endpointUrl
: DEFAULT_ENDPOINT_URL;

// 1. add channel
await addChannelAction(options.channelId);

// 2. create liff app (@ server)
const liffId = await createLiffApp(options);

// 3. create liff app (@ client)
execSync(`npx @line/create-liff-app ${options.name} -l ${liffId}`, {
stdio: "inherit",
});

// 4. print instructions on how to run locally
console.info(`App ${liffId} successfully created.

Now do the following:
1. go to app directory: \`cd ${options.name}\`
2. create certificate key files (e.g. \`mkcert localhost\`, see: https://developers.line.biz/en/docs/liff/liff-cli/#serve-operating-conditions )
3. run LIFF app template using command above (e.g. \`npm run dev\` or \`yarn dev\`)
4. open new terminal window, navigate to \`${options.name}\` directory
5. run \`liff-cli serve -l ${liffId} -u http://localhost:\${PORT FROM STEP 3.}/\`
6. open browser and navigate to http://localhost:\${PORT FROM STEP 3.}/
`);
};

export const installInitCommands = (program: Command) => {
const app = program.command("init");
app
.description("Initialize new LIFF app")
.option(
"-c, --channel-id [channelId]",
"The channel ID to use. If it isn't specified, the currentChannelId will be used.",
)
.option("-n, --name <name>", "The name of the LIFF app")
.option(
"-v, --view-type <viewType>",
"The view type of the LIFF app. Must be 'compact', 'tall', or 'full'",
)
.option(
"-e, --endpoint-url <endpointUrl>",
"The endpoint URL of the LIFF app. Must be 'https://'",
)
.action(addAction);
};
1 change: 1 addition & 0 deletions src/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Options:
Commands:
channel Manage LIFF channels
app Manage LIFF apps
init [options] Initialize new LIFF app
serve [options] Manage HTTPS dev server
help [command] display help for command
`);
Expand Down
2 changes: 2 additions & 0 deletions src/setup.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Command } from "commander";
import { installChannelCommands } from "./channel/commands/index.js";
import { installAppCommands } from "./app/commands/index.js";
import { installInitCommands } from "./init/index.js";
import { serveCommands } from "./serve/index.js";

export const setupCLI = (program: Command) => {
installChannelCommands(program);
installAppCommands(program);
installInitCommands(program);
serveCommands(program);
// TODO .version?
return {
Expand Down