Skip to content

Commit

Permalink
feat(cli): auto detect framework's webDir (#4550)
Browse files Browse the repository at this point in the history
co-authored-by: Ely Lucas <ely@meta-tek.net>
  • Loading branch information
adamdbradley and elylucas authored May 18, 2021
1 parent 90712bc commit 329448a
Showing 4 changed files with 265 additions and 0 deletions.
6 changes: 6 additions & 0 deletions cli/src/definitions.ts
Original file line number Diff line number Diff line change
@@ -118,3 +118,9 @@ export interface Config {
readonly cli: CLIConfig;
readonly app: AppConfig;
}

export interface FrameworkConfig {
name: string;
isMatch: (config: Config) => boolean;
webDir: string;
}
92 changes: 92 additions & 0 deletions cli/src/framework-configs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { Config, FrameworkConfig } from './definitions';

const FRAMEWORK_CONFIGS: FrameworkConfig[] = [
{
name: 'Angular',
isMatch: config =>
hasDependency(config, '@angular/cli') &&
!hasDependency(config, '@ionic/angular'),
webDir: 'dist',
},
{
name: 'Create React App',
isMatch: config =>
hasDependency(config, 'react-scripts') &&
hasDependency(config, 'react-dev-utils') &&
!hasDependency(config, '@ionic/react'),
webDir: 'build',
},
{
name: 'Ember',
isMatch: config => hasDependency(config, 'ember-cli'),
webDir: 'dist',
},
{
name: 'Gatsby',
isMatch: config => hasDependency(config, 'gatsby'),
webDir: 'public',
},
{
name: 'Ionic Angular',
isMatch: config => hasDependency(config, '@ionic/angular'),
webDir: 'www',
},
{
name: 'Ionic React',
isMatch: config => hasDependency(config, '@ionic/react'),
webDir: 'build',
},
{
name: 'Ionic Vue',
isMatch: config => hasDependency(config, '@ionic/vue'),
webDir: 'public',
},
{
name: 'Next',
isMatch: config => hasDependency(config, 'next'),
webDir: 'public',
},
{
name: 'Preact',
isMatch: config => hasDependency(config, 'preact-cli'),
webDir: 'build',
},
{
name: 'Stencil',
isMatch: config => hasDependency(config, '@stencil/core'),
webDir: 'www',
},
{
name: 'Svelte',
isMatch: config =>
hasDependency(config, 'svelte') && hasDependency(config, 'sirv-cli'),
webDir: 'public',
},
{
name: 'Vue',
isMatch: config =>
hasDependency(config, '@vue/cli-service') &&
!hasDependency(config, '@ionic/vue'),
webDir: 'dist',
},
];

export function detectFramework(config: Config): FrameworkConfig | undefined {
return FRAMEWORK_CONFIGS.find(f => f.isMatch(config));
}

function hasDependency(config: Config, depName: string): boolean {
const deps = getDependencies(config);
return deps.includes(depName);
}

function getDependencies(config: Config): string[] {
const deps: string[] = [];
if (config?.app?.package?.dependencies) {
deps.push(...Object.keys(config.app.package.dependencies));
}
if (config?.app?.package?.devDependencies) {
deps.push(...Object.keys(config.app.package.devDependencies));
}
return deps;
}
6 changes: 6 additions & 0 deletions cli/src/tasks/init.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import {
} from '../config';
import { getCordovaPreferences } from '../cordova';
import { fatal, isFatal } from '../errors';
import { detectFramework } from '../framework-configs';
import { output, logSuccess, logPrompt } from '../log';
import { readConfig, writeConfig as sysWriteConfig } from '../sysconfig';
import { resolveNode } from '../util/node';
@@ -115,6 +116,11 @@ async function getAppId(config: Config, id: string) {

async function getWebDir(config: Config, webDir?: string) {
if (!webDir) {
const framework = detectFramework(config);
if (framework?.webDir) {
return framework.webDir;
}

const answers = await logPrompt(
`${c.strong(`What is the web asset directory for your app?`)}\n` +
`This directory should contain the final ${c.strong(
161 changes: 161 additions & 0 deletions cli/test/framework-detection.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { resolve } from 'path';

import { detectFramework } from '../../cli/src/framework-configs';
import type { Config } from '../src/definitions';

describe('framework detection', () => {
let config: Config;

beforeEach(() => {
config = {
cli: null as any,
app: {
rootDir: resolve('/'),
appId: 'appId',
appName: 'appName',
webDir: '',
webDirAbs: '',
package: {
name: 'package-name',
version: '0.0.0',
},
extConfigType: 'json',
extConfigName: '',
extConfigFilePath: '',
extConfig: null as any,
bundledWebRuntime: true,
},
android: null as any,
ios: null as any,
web: null as any,
};
});

it('Angular', () => {
addDep(config, '@angular/cli');
const f = detectFramework(config);
expect(f?.name).toBe('Angular');
expect(f?.webDir).toBe('dist');
});

it('Create React App', () => {
addDep(config, 'react-scripts');
addDep(config, 'react-dev-utils');
const f = detectFramework(config);
expect(f?.name).toBe('Create React App');
expect(f?.webDir).toBe('build');
});

it('Ember', () => {
addDep(config, 'ember-cli');
const f = detectFramework(config);
expect(f?.name).toBe('Ember');
expect(f?.webDir).toBe('dist');
});

it('Gatsby', () => {
addDep(config, 'gatsby');
const f = detectFramework(config);
expect(f?.name).toBe('Gatsby');
expect(f?.webDir).toBe('public');
});

it('Ionic Angular', () => {
addDep(config, '@ionic/angular');
const f = detectFramework(config);
expect(f?.name).toBe('Ionic Angular');
expect(f?.webDir).toBe('www');
});

it('Ionic Angular and not just Angular', () => {
addDep(config, '@angular/cli');
addDep(config, '@ionic/angular');
const f = detectFramework(config);
expect(f?.name).toBe('Ionic Angular');
expect(f?.webDir).toBe('www');
});

it('Ionic React', () => {
addDep(config, '@ionic/react');
const f = detectFramework(config);
expect(f?.name).toBe('Ionic React');
expect(f?.webDir).toBe('build');
});

it('Ionic React over Create React App', () => {
addDep(config, '@ionic/react');
addDep(config, 'react-scripts');
addDep(config, 'react-dev-utils');
const f = detectFramework(config);
expect(f?.name).toBe('Ionic React');
expect(f?.webDir).toBe('build');
});

it('Ionic Vue', () => {
addDep(config, '@ionic/vue');
const f = detectFramework(config);
expect(f?.name).toBe('Ionic Vue');
expect(f?.webDir).toBe('public');
});

it('Ionic Vue and not just Vue', () => {
addDep(config, '@ionic/vue');
addDep(config, '@vue/cli-service');
const f = detectFramework(config);
expect(f?.name).toBe('Ionic Vue');
expect(f?.webDir).toBe('public');
});

it('Next', () => {
addDep(config, 'next');
const f = detectFramework(config);
expect(f?.name).toBe('Next');
expect(f?.webDir).toBe('public');
});

it('Preact', () => {
addDep(config, 'preact-cli');
const f = detectFramework(config);
expect(f?.name).toBe('Preact');
expect(f?.webDir).toBe('build');
});

it('Stencil', () => {
addDep(config, '@stencil/core');
const f = detectFramework(config);
expect(f?.name).toBe('Stencil');
expect(f?.webDir).toBe('www');
});

it('Svelte', () => {
addDep(config, 'svelte');
addDep(config, 'sirv-cli');
const f = detectFramework(config);
expect(f?.name).toBe('Svelte');
expect(f?.webDir).toBe('public');
});

it('not Svelte w/out sirv-cli', () => {
addDep(config, 'svelte');
const f = detectFramework(config);
expect(f).toBeUndefined();
});

it('Vue', () => {
addDep(config, '@vue/cli-service');
const f = detectFramework(config);
expect(f?.name).toBe('Vue');
expect(f?.webDir).toBe('dist');
});

it('nothing detected', () => {
const f = detectFramework(config);
expect(f).toBeUndefined();
});
});

function addDep(config: Config, depName: string) {
(config.app.package as any).dependencies =
config.app.package.dependencies || {};
(config.app.package.dependencies as any)[depName] = '0.0.0';
}

0 comments on commit 329448a

Please sign in to comment.