1import { Logger } from '@expo/xdl';
2import chalk from 'chalk';
3import * as fs from 'fs-extra';
4import pacote from 'pacote';
5import * as path from 'path';
6
7const DEFAULT_TEMPLATE = 'expo-module-template@latest';
8
9/**
10 * Fetches directory from npm or given templateDirectory into destinationPath
11 * @param destinationPath - destination for fetched template
12 * @param template - optional template provided as npm package or local directory
13 */
14export default async function fetchTemplate(destinationPath: string, template?: string) {
15  if (template && fs.existsSync(path.resolve(template))) {
16    // local template
17    Logger.global.info(`Using local template: ${chalk.bold(path.resolve(template))}.`);
18    await fs.copy(path.resolve(template), destinationPath);
19  } else if (template && isNpmPackage(template)) {
20    // npm package
21    Logger.global.info(`Using NPM package as template: ${chalk.bold(template)}`);
22    await pacote.extract(template, destinationPath);
23  } else {
24    // default npm packge
25    Logger.global.info(`Using default NPM package as template: ${chalk.bold(DEFAULT_TEMPLATE)}`);
26    await pacote.extract(DEFAULT_TEMPLATE, destinationPath);
27  }
28
29  if (await fs.pathExists(path.join(destinationPath, 'template-unimodule.json'))) {
30    await fs.move(
31      path.join(destinationPath, 'template-unimodule.json'),
32      path.join(destinationPath, 'unimodule.json')
33    );
34  }
35}
36
37function isNpmPackage(template: string) {
38  return (
39    !template.match(/^\./) && // don't start with .
40    !template.match(/^_/) && // don't start with _
41    template.toLowerCase() === template && // only lowercase
42    !/[~'!()*]/.test(template.split('/').slice(-1)[0]) && // don't contain any character from [~'!()*]
43    template.match(/^(@([^/]+?)\/)?([^/@]+)(@(((\d\.\d\.\d)(-[^/@]+)?)|latest|next))?$/) // has shape (@scope/)?actual-package-name(@0.1.1(-tag.1)?|tag-name)?
44  );
45}
46