Skip to content

Commit ad6c483

Browse files
mhartingtonmlynch
andauthored
feat(): support vue projects (#4515)
* feat(): support vue projects * chore(): update repo * Add theme support for Vue apps from wizard and default capacitor on for all apps * fix(vue): adjust cap integration Co-authored-by: Max Lynch <max@ionic.io>
1 parent b8e0630 commit ad6c483

File tree

6 files changed

+172
-35
lines changed

6 files changed

+172
-35
lines changed

packages/@ionic/cli/src/commands/start.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,9 +539,11 @@ Use the ${input('--type')} option to start projects using older versions of Ioni
539539
this.env.shell.alterPath = p => prependNodeModulesBinToPath(projectDir, p);
540540

541541
if (!this.schema.cloned) {
542-
if (this.schema.type === 'react') {
543-
options['capacitor'] = true;
544-
}
542+
// Default to capacitor always
543+
if (this.schema.type === 'react' || this.schema.type === 'vue') {
544+
options['capacitor'] = true;
545+
}
546+
545547

546548
if (options['cordova']) {
547549
const { confirmCordovaUsage } = await import('../lib/integrations/cordova/utils');

packages/@ionic/cli/src/definitions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,6 @@ export interface ReactBuildOptions extends BuildOptions<'react'> {
644644
export interface VueBuildOptions extends BuildOptions<'vue'> {
645645
configuration?: string;
646646
sourcemaps?: boolean;
647-
cordovaAssets?: boolean;
648647
}
649648

650649
export interface IonicCapacitorOptions {
@@ -723,7 +722,8 @@ export interface ReactServeOptions extends ServeOptions {
723722
}
724723

725724
export interface VueServeOptions extends ServeOptions {
726-
ssl?: boolean;
725+
https: boolean;
726+
mode: string;
727727
configuration?: string;
728728
sourcemaps?: boolean;
729729
}
Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import * as chalk from 'chalk';
2-
31
import { CommandLineInputs, CommandLineOptions, CommandMetadata, VueBuildOptions } from '../../../definitions';
4-
import { BuildRunner, BuildRunnerDeps } from '../../build';
5-
import { RunnerException } from '../../errors';
2+
import { BUILD_SCRIPT, BuildCLI, BuildRunner, BuildRunnerDeps } from '../../build';
3+
4+
import { VueProject } from './';
65

6+
export interface VueBuildRunnerDeps extends BuildRunnerDeps {
7+
readonly project: VueProject;
8+
}
79
export class VueBuildRunner extends BuildRunner<VueBuildOptions> {
8-
constructor(protected readonly e: BuildRunnerDeps) {
10+
constructor(protected readonly e: VueBuildRunnerDeps) {
911
super();
1012
}
1113

@@ -23,15 +25,31 @@ export class VueBuildRunner extends BuildRunner<VueBuildOptions> {
2325
}
2426

2527
async buildProject(options: VueBuildOptions): Promise<void> {
26-
const cli = this.getPkgManagerBuildCLI();
28+
const vueScripts = new VueBuildCLI(this.e);
29+
await vueScripts.build(options);
30+
}
31+
}
2732

28-
if (!await cli.resolveScript()) {
29-
throw new RunnerException(
30-
`Cannot perform build.\n` +
31-
`Since you're using the ${chalk.bold('Vue')} project type, you must provide the ${chalk.green(cli.script)} npm script so the Ionic CLI can build your project.`
32-
);
33+
export class VueBuildCLI extends BuildCLI<VueBuildOptions> {
34+
readonly name = 'Vue CLI Service';
35+
readonly pkg = '@vue/cli-service';
36+
readonly program = 'vue-cli-service';
37+
readonly prefix = 'vue-cli-service';
38+
readonly script = BUILD_SCRIPT;
39+
40+
protected async buildArgs(options: VueBuildOptions): Promise<string[]> {
41+
const { pkgManagerArgs } = await import('../../utils/npm');
42+
43+
if (this.resolvedProgram === this.program) {
44+
return ['build'];
45+
} else {
46+
const [ , ...pkgArgs ] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script });
47+
return pkgArgs;
3348
}
49+
}
3450

35-
await cli.build(options);
51+
protected async buildEnvVars(options: VueBuildOptions): Promise<NodeJS.ProcessEnv> {
52+
const env: NodeJS.ProcessEnv = {};
53+
return { ...await super.buildEnvVars(options), ...env };
3654
}
3755
}

packages/@ionic/cli/src/lib/project/vue/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as chalk from 'chalk';
22
import * as Debug from 'debug';
33
import * as lodash from 'lodash';
4+
import * as path from 'path';
45

56
import { Project } from '../';
67
import { InfoItem } from '../../../definitions';
@@ -13,7 +14,7 @@ export class VueProject extends Project {
1314

1415
async getInfo(): Promise<InfoItem[]> {
1516
const [
16-
[ ionicVuePkg, ionicVuePkgPath ],
17+
[ionicVuePkg, ionicVuePkgPath],
1718
] = await Promise.all([
1819
this.getPackageJson('@ionic/vue'),
1920
]);
@@ -71,4 +72,9 @@ export class VueProject extends Project {
7172
`Since you're using the ${chalk.bold('Vue')} project type, this command won't work. The Ionic CLI doesn't know how to generate framework components for Vue projects.`
7273
);
7374
}
75+
76+
setPrimaryTheme(themeColor: string): Promise<void> {
77+
const themePath = path.join(this.directory, 'src', 'theme', 'variables.css');
78+
return this.writeThemeColor(themePath, themeColor);
79+
}
7480
}
Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import { stripAnsi } from '@ionic/cli-framework-output';
12
import { findClosestOpenPort } from '@ionic/utils-network';
2-
import * as chalk from 'chalk';
33

44
import { CommandMetadata, ServeDetails, VueServeOptions } from '../../../definitions';
5-
import { RunnerException } from '../../errors';
6-
import { BIND_ALL_ADDRESS, LOCAL_ADDRESSES, ServeRunner, ServeRunnerDeps } from '../../serve';
5+
import { strong } from '../../color';
6+
import { BIND_ALL_ADDRESS, DEFAULT_ADDRESS, LOCAL_ADDRESSES, SERVE_SCRIPT, ServeCLI, ServeRunner, ServeRunnerDeps } from '../../serve';
77

88
export class VueServeRunner extends ServeRunner<VueServeOptions> {
99
constructor(protected readonly e: ServeRunnerDeps) {
@@ -14,34 +14,112 @@ export class VueServeRunner extends ServeRunner<VueServeOptions> {
1414
return {};
1515
}
1616

17-
modifyOpenUrl(url: string, options: VueServeOptions): string {
17+
modifyOpenUrl(url: string, _options: VueServeOptions): string {
1818
return url;
1919
}
2020

2121
async serveProject(options: VueServeOptions): Promise<ServeDetails> {
22-
const cli = this.getPkgManagerServeCLI();
23-
24-
if (!await cli.resolveScript()) {
25-
throw new RunnerException(
26-
`Cannot perform serve.\n` +
27-
`Since you're using the ${chalk.bold('Vue')} project type, you must provide the ${chalk.green(cli.script)} npm script so the Ionic CLI can serve your project.`
28-
);
29-
}
30-
31-
const [ externalIP, availableInterfaces ] = await this.selectExternalIP(options);
22+
const [externalIP, availableInterfaces] = await this.selectExternalIP(options);
3223

3324
const port = options.port = await findClosestOpenPort(options.port);
3425

35-
await cli.serve(options);
26+
const vueScripts = new VueServeCLI(this.e);
27+
await vueScripts.serve(options);
3628

3729
return {
38-
custom: false,
39-
protocol: 'http',
30+
custom: vueScripts.resolvedProgram !== vueScripts.program,
31+
protocol: options.https ? 'https' : 'http',
4032
localAddress: 'localhost',
4133
externalAddress: externalIP,
4234
externalNetworkInterfaces: availableInterfaces,
4335
port,
4436
externallyAccessible: ![BIND_ALL_ADDRESS, ...LOCAL_ADDRESSES].includes(externalIP),
4537
};
4638
}
39+
40+
}
41+
42+
export class VueServeCLI extends ServeCLI<VueServeOptions> {
43+
readonly name = 'Vue CLI Service';
44+
readonly pkg = '@vue/cli-service';
45+
readonly program = 'vue-cli-service';
46+
readonly prefix = 'vue-cli-service';
47+
readonly script = SERVE_SCRIPT;
48+
protected chunks = 0;
49+
50+
async serve(options: VueServeOptions): Promise<void> {
51+
this.on('compile', chunks => {
52+
if (chunks > 0) {
53+
this.e.log.info(`... and ${strong(chunks.toString())} additional chunks`);
54+
}
55+
});
56+
57+
return super.serve(options);
58+
}
59+
60+
protected stdoutFilter(line: string): boolean {
61+
if (this.resolvedProgram !== this.program) {
62+
return super.stdoutFilter(line);
63+
}
64+
65+
const strippedLine = stripAnsi(line);
66+
const compileMsgs = ['Compiled successfully', 'Compiled with warnings', 'Failed to compile'];
67+
if (compileMsgs.some(msg => strippedLine.includes(msg))) {
68+
this.emit('ready');
69+
return false;
70+
}
71+
72+
if (strippedLine.match(/.*chunk\s{\d+}.+/)) {
73+
this.chunks++;
74+
return false;
75+
}
76+
77+
if (strippedLine.includes('Compiled successfully')) {
78+
this.emit('compile', this.chunks);
79+
this.chunks = 0;
80+
}
81+
82+
// const endBannerMsgs = ['development build', 'production build']
83+
// if (endBannerMsgs.some(msg => strippedLine.includes(msg))) {
84+
// return false;
85+
// }
86+
return true;
87+
}
88+
protected stderrFilter(line: string): boolean {
89+
if (this.resolvedProgram !== this.program) {
90+
return super.stderrFilter(line);
91+
}
92+
const strippedLine = stripAnsi(line);
93+
if (strippedLine.includes('webpack.Progress')) {
94+
return false;
95+
}
96+
97+
return true;
98+
99+
}
100+
101+
protected async buildArgs(_options: VueServeOptions): Promise<string[]> {
102+
const { pkgManagerArgs } = await import('../../utils/npm');
103+
104+
if (this.resolvedProgram === this.program) {
105+
return ['serve'];
106+
} else {
107+
const [, ...pkgArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'run', script: this.script });
108+
return pkgArgs;
109+
}
110+
}
111+
112+
protected async buildEnvVars(options: VueServeOptions): Promise<NodeJS.ProcessEnv> {
113+
const env: NodeJS.ProcessEnv = {};
114+
// // Vue CLI binds to `localhost` by default, but if specified it prints a
115+
// // warning, so don't set `HOST` if the host is set to `localhost`.
116+
if (options.host !== DEFAULT_ADDRESS) {
117+
env.HOST = options.host;
118+
}
119+
120+
env.PORT = String(options.port);
121+
env.HTTPS = options.https ? 'true' : 'false';
122+
123+
return { ...await super.buildEnvVars(options), ...env };
124+
}
47125
}

packages/@ionic/cli/src/lib/start.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,42 @@ export const SUPPORTED_FRAMEWORKS: readonly SupportedFramework[] = [
199199
type: 'react',
200200
description: 'https://reactjs.org',
201201
},
202+
{
203+
name: 'Vue',
204+
type: 'vue',
205+
description: 'https://vuejs.org',
206+
},
202207
];
203208

204209
export const STARTER_TEMPLATES: StarterTemplate[] = [
210+
{
211+
name: 'tabs',
212+
projectType: 'vue',
213+
type: 'managed',
214+
description: 'A starting project with a simple tabbed interface',
215+
id: 'vue-official-tabs',
216+
},
217+
{
218+
name: 'sidemenu',
219+
projectType: 'vue',
220+
type: 'managed',
221+
description: 'A starting project with a side menu with navigation in the content area',
222+
id: 'vue-official-sidemenu',
223+
},
224+
{
225+
name: 'blank',
226+
projectType: 'vue',
227+
type: 'managed',
228+
description: 'A blank starter project',
229+
id: 'vue-official-blank',
230+
},
231+
{
232+
name: 'list',
233+
projectType: 'vue',
234+
type: 'managed',
235+
description: 'A starting project with a list',
236+
id: 'vue-official-list',
237+
},
205238
{
206239
name: 'tabs',
207240
projectType: 'angular',

0 commit comments

Comments
 (0)