@line/create-liff-app
Version:
Start developing LIFF application with a simple CLI command.
363 lines (359 loc) • 12.9 kB
JavaScript
;
/* Copyright 2023 LY Corporation
* LY Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
* https://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.templateNames = exports.createLiffApp = exports.init = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const chalk_1 = __importDefault(require("chalk"));
const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
const inquirer_1 = __importDefault(require("inquirer"));
const rename = {
'.gitignore.default': '.gitignore'
};
function init(answers = {}) {
console.log(`${chalk_1.default.greenBright('Welcome')} to the ${chalk_1.default.cyan('Create LIFF App')}`);
prompt(questions, answers).then(async (answers) => await createLiffApp(answers));
}
exports.init = init;
async function createLiffApp(answers) {
const { projectName, template, language, liffId } = answers;
const templateConfig = templates[template];
if (!templateConfig) {
throw new Error(`Invalid template name: ${template}`);
}
const isTypescript = language === 'TypeScript';
const cwd = process.cwd();
const root = path_1.default.join(cwd, projectName);
const packageManager = answers.packageManager;
const isYarn = packageManager === 'yarn';
try {
if (templateConfig?.getCreateAppScript) {
// generate project using `create-app`
const script = templateConfig.getCreateAppScript({ isTypescript, isYarn, projectName });
console.log('\nGenerating liff app using `create-app`, this might take a while.\n');
await executeCreateAppScript(script);
}
else {
// create directory
fs_1.default.mkdirSync(root, { recursive: true });
}
// copy files
const templateName = `${template}${isTypescript ? '-ts' : ''}`;
const templateDir = path_1.default.join(__dirname, '../templates', templateName);
const files = fs_1.default.readdirSync(templateDir);
for (const file of files.filter(f => f !== 'package.json')) {
const src = path_1.default.join(templateDir, file);
const dest = rename[file] ? path_1.default.join(root, rename[file]) : path_1.default.join(root, file);
copy(src, dest);
}
if (!templateConfig?.getCreateAppScript) {
// create package.json
const packageName = isValidPackageName(projectName) ? projectName : toValidPackageName(projectName);
const pkg = require(path_1.default.join(templateDir, 'package.json'));
pkg.name = packageName;
fs_1.default.writeFileSync(path_1.default.join(root, 'package.json'), JSON.stringify(pkg, null, 2));
}
// create .env file
const content = `${templateConfig.envPrefix}LIFF_ID=${liffId}`;
const envFileName = templateConfig?.envFileNameVariant || '.env';
fs_1.default.writeFileSync(path_1.default.join(root, envFileName), content);
// install
const { dependencies, devDependencies, tsDevDependencies } = templateConfig;
if (isTypescript)
devDependencies.push(...tsDevDependencies);
console.log('\nInstalling dependencies:');
dependencies.forEach((dependency) => console.log(`- ${chalk_1.default.blue(dependency)}`));
console.log();
await install({ root, isYarn, dependencies, isDev: false });
if (devDependencies.length) {
console.log('\nInstalling devDependencies:');
devDependencies.forEach((dependency) => console.log(`- ${chalk_1.default.blue(dependency)}`));
console.log();
await install({ root, isYarn, dependencies: devDependencies, isDev: true });
}
// Done
showDoneComments({ projectName, isYarn });
}
catch (error) {
console.error(error);
process.exit(1);
}
}
exports.createLiffApp = createLiffApp;
function copy(src, dest) {
const stat = fs_1.default.statSync(src);
if (stat.isDirectory()) {
copyDir(src, dest);
}
else {
fs_1.default.copyFileSync(src, dest);
}
}
function copyDir(srcDir, destDir) {
fs_1.default.mkdirSync(destDir, { recursive: true });
for (const file of fs_1.default.readdirSync(srcDir)) {
const srcFile = path_1.default.resolve(srcDir, file);
const destFile = path_1.default.resolve(destDir, file);
copy(srcFile, destFile);
}
}
function isValidPackageName(name) {
const { validForNewPackages } = (0, validate_npm_package_name_1.default)(name);
return validForNewPackages;
}
function toValidPackageName(name) {
return name
.trim()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/^[._]/, '')
.replace(/[^a-z0-9-~]+/g, '-');
}
function executeCreateAppScript(script) {
return new Promise((resolve, reject) => {
try {
const [command, ...args] = script;
const child = (0, cross_spawn_1.default)(command, args, {
stdio: 'inherit',
env: { ...process.env, ADBLOCK: '1', DISABLE_OPENCOLLECTIVE: '1' },
});
child.on('close', (code) => {
if (code !== 0) {
reject({ command: `${command} ${args.join(' ')}` });
return;
}
resolve();
});
}
catch (error) {
reject(`Error occurred during installation: ${error}`);
}
});
}
function install({ root, dependencies, isYarn, isDev, }) {
return new Promise((resolve, reject) => {
try {
const command = isYarn ? 'yarnpkg' : 'npm';
const args = [];
if (isYarn) {
args.push('add', '--exact', '--cwd', root);
if (isDev)
args.push('--dev');
}
else {
args.push('install', '--save-exact', '--prefix', root);
if (isDev)
args.push('--save-dev');
}
args.push(...dependencies);
const child = (0, cross_spawn_1.default)(command, args, {
stdio: 'inherit',
env: { ...process.env, ADBLOCK: '1', DISABLE_OPENCOLLECTIVE: '1' }
});
child.on('close', code => {
if (code !== 0) {
reject({ command: `${command} ${args.join(' ')}` });
return;
}
resolve();
});
}
catch (error) {
reject(`Error occurred during installation: ${error}`);
}
});
}
function showDoneComments({ projectName, isYarn }) {
console.log('\n\nDone! Now run: \n');
console.log(` cd ${chalk_1.default.blue(projectName)}`);
if (isYarn) {
console.log(' yarn dev\n\n');
}
else {
console.log(' npm run dev\n\n');
}
}
const prompt = inquirer_1.default.createPromptModule();
const questions = [
{
type: 'input',
name: 'projectName',
message: 'Enter your project name: ',
default: 'my-app',
validate: (input) => {
const projectName = input.trim();
if (!projectName) {
console.log(`\n${chalk_1.default.yellow('Project name is required.')}`);
return false;
}
if (fs_1.default.existsSync(path_1.default.basename(projectName)) && fs_1.default.readdirSync(projectName).length !== 0) {
console.log(`\n${chalk_1.default.yellow('The project is already exists.')}`);
return false;
}
return true;
},
},
{
type: 'list',
name: 'template',
message: 'Which template do you want to use?',
choices: [
{
value: 'vanilla',
checked: true,
},
{
value: 'react',
checked: false,
},
{
value: 'vue',
checked: false,
},
{
value: 'svelte',
checked: false,
},
{
value: 'nextjs',
checked: false,
},
{
value: 'nuxtjs',
checked: false,
},
],
},
{
type: 'list',
name: 'language',
message: 'JavaScript or TypeScript?',
choices: [
{
value: 'JavaScript',
checked: true
},
{
value: 'TypeScript',
checked: false
}
]
},
{
type: 'input',
name: 'liffId',
message: `Please enter your LIFF ID: \n ${chalk_1.default.gray('Don\'t you have LIFF ID? Check out https://developers.line.biz/ja/docs/liff/getting-started/')}`,
default: 'liffId',
validate: (input) => {
const liffId = input.trim();
if (!liffId) {
console.log();
console.log(`${chalk_1.default.yellow('LIFF ID is required.')}`);
return false;
}
return true;
}
},
{
type: 'list',
name: 'packageManager',
message: 'Which package manager do you want to use?',
choices: [
{
key: 'yarn',
value: 'yarn',
checked: true,
},
{
key: 'npm',
value: 'npm',
checked: false,
},
],
}
];
const templates = {
vanilla: {
envPrefix: 'VITE_',
dependencies: ['@line/liff'],
devDependencies: ['vite'],
tsDevDependencies: ['typescript'],
},
react: {
envPrefix: 'VITE_',
dependencies: ['@line/liff', 'react', 'react-dom'],
devDependencies: ['@vitejs/plugin-react', 'vite'],
tsDevDependencies: ['@types/react', '@types/react-dom', 'typescript'],
},
vue: {
envPrefix: 'VITE_',
dependencies: ['@line/liff', 'vue'],
devDependencies: ['@vitejs/plugin-vue', 'vite'],
tsDevDependencies: ['typescript', 'vue-tsc'],
},
svelte: {
envPrefix: 'VITE_',
dependencies: ['@line/liff'],
devDependencies: ['@sveltejs/vite-plugin-svelte', 'svelte', 'vite'],
tsDevDependencies: ['@tsconfig/svelte', 'svelte-check', 'svelte-preprocess', 'tslib', 'typescript'],
},
nextjs: {
envPrefix: 'NEXT_PUBLIC_',
envFileNameVariant: '.env.local',
dependencies: ['@line/liff'],
devDependencies: [],
tsDevDependencies: [],
getCreateAppScript: ({ isTypescript, isYarn, projectName }) => {
const script = [];
if (isYarn) {
script.push('yarnpkg', 'create', 'next-app');
}
else {
script.push('npx', 'create-next-app', '--use-npm');
}
script.push(projectName);
if (isTypescript)
script.push('--ts');
return script;
},
},
nuxtjs: {
envPrefix: '',
dependencies: ['@line/liff'],
devDependencies: [],
tsDevDependencies: [],
getCreateAppScript: ({ isTypescript, isYarn, projectName }) => {
const answers = {
name: projectName,
pm: isYarn ? 'yarn' : 'npm',
language: isTypescript ? 'ts' : 'js',
features: ['axios'],
linter: ['eslint'],
ui: 'none',
test: 'none',
mode: 'universal',
target: 'server',
template: 'html',
devTools: 'none',
vcs: 'none',
};
const script = ['npx', 'create-nuxt-app', projectName, '--answers', JSON.stringify(answers)];
return script;
},
},
};
exports.templateNames = Object.keys(templates);