Skip to content

Commit

Permalink
Cleaning up the ng-add command a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesdaniels committed May 18, 2021
1 parent 3a4854d commit dbda308
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 109 deletions.
31 changes: 22 additions & 9 deletions src/schematics/ng-add-ssr.ts
@@ -1,16 +1,18 @@
import { SchematicsException, Tree, SchematicContext } from '@angular-devkit/schematics';
import { SchematicsException, Tree, SchematicContext, noop } from '@angular-devkit/schematics';
import {
addDependencies,
DeployOptions,
generateFirebaseRc,
NgAddNormalizedOptions,
overwriteIfExists,
safeReadJSON,
stringifyFormatted
} from './ng-add-common';
import { FirebaseJSON, Workspace, WorkspaceProject } from './interfaces';

import { default as defaultDependencies, firebaseFunctions as firebaseFunctionsDependencies } from './versions.json';
import { getProject, projectTypePrompt } from './utils';
import { firebaseFunctions as firebaseFunctionsDependencies } from './versions.json';
import { dirname, join } from 'path';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';

// We consider a project to be a universal project if it has a `server` architect
// target. If it does, it knows how to build the application's server.
Expand Down Expand Up @@ -96,12 +98,23 @@ export function generateFirebaseJson(
overwriteIfExists(tree, path, stringifyFormatted(firebaseJson));
}

export const addFirebaseFunctionsDependencies = (tree: Tree, context: SchematicContext) => {
addDependencies(
tree,
firebaseFunctionsDependencies,
context
);
export const addFirebaseFunctionsDependencies = (options: DeployOptions) => (tree: Tree, context: SchematicContext) => {
const {project} = getProject(options, tree);
projectTypePrompt(project).then(({universalProject}) => {
if (universalProject) {
(global as any).setupAsAngularUniversalApp = true;
addDependencies(
tree,
firebaseFunctionsDependencies,
context
);
context.addTask(new NodePackageInstallTask());
return tree;
} else {
(global as any).setupAsAngularUniversalApp = false;
return noop();
}
});
};

export const setupUniversalDeployment = (config: {
Expand Down
5 changes: 4 additions & 1 deletion src/schematics/ng-add-static.ts
Expand Up @@ -10,6 +10,7 @@ import {
import { FirebaseJSON, Workspace, WorkspaceProject } from './interfaces';

import { default as defaultDependencies } from './versions.json';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';

function emptyFirebaseJson() {
return {
Expand Down Expand Up @@ -72,12 +73,14 @@ export function generateFirebaseJson(
overwriteIfExists(tree, path, stringifyFormatted(firebaseJson));
}

export const addFirebaseHostingDependencies = (tree: Tree, context: SchematicContext) => {
export const addFirebaseHostingDependencies = () => async (tree: Tree, context: SchematicContext) => {
addDependencies(
tree,
defaultDependencies,
context
);
context.addTask(new NodePackageInstallTask());
return tree;
};

export const setupStaticDeployment = (config: {
Expand Down
117 changes: 20 additions & 97 deletions src/schematics/ng-add.ts
@@ -1,66 +1,11 @@
import { SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask, RunSchematicTask } from '@angular-devkit/schematics/tasks';
import { listProjects, projectPrompt, projectTypePrompt } from './utils';
import { Workspace } from './interfaces';
import { chain, Tree } from '@angular-devkit/schematics';
import { listProjects, projectPrompt, getWorkspace, getProject } from './utils';
import { DeployOptions, NgAddNormalizedOptions } from './ng-add-common';
import { addFirebaseFunctionsDependencies, setupUniversalDeployment } from './ng-add-ssr';
import { addFirebaseHostingDependencies, setupStaticDeployment } from './ng-add-static';

function getWorkspace(
host: Tree
): { path: string; workspace: Workspace } {
const possibleFiles = ['/angular.json', '/.angular.json'];
const path = possibleFiles.filter(p => host.exists(p))[0];

const configBuffer = host.read(path);
if (configBuffer === null) {
throw new SchematicsException(`Could not find angular.json`);
}

// We can not depend on this library to have be included in older (or newer) Angular versions.
// Require here, since the schematic will add it to the package.json and install it before
// continuing.
const { parse }: typeof import('jsonc-parser') = require('jsonc-parser');

const workspace = parse(configBuffer.toString()) as Workspace|undefined;
if (!workspace) {
throw new SchematicsException('Could not parse angular.json');
}

return {
path,
workspace
};
}

const getProject = (options: DeployOptions, host: Tree) => {
const { workspace } = getWorkspace(host);
const projectName = options.project || workspace.defaultProject;

if (!projectName) {
throw new SchematicsException(
'No Angular project selected and no default project in the workspace'
);
}

const project = workspace.projects[projectName];
if (!project) {
throw new SchematicsException(
'The specified Angular project is not defined in this workspace'
);
}

if (project.projectType !== 'application') {
throw new SchematicsException(
`Deploy requires an Angular project type of "application" in angular.json`
);
}

return {project, projectName};
};

export const setupProject =
(host: Tree, options: DeployOptions & { isUniversalProject: boolean, firebaseProject: string }) => {
(host: Tree, options: DeployOptions & { firebaseProject: string, isUniversalProject: boolean }) => {
const { path: workspacePath, workspace } = getWorkspace(host);

const {project, projectName} = getProject(options, host);
Expand All @@ -78,50 +23,28 @@ export const setupProject =
tree: host,
project
});
} else {
return setupStaticDeployment({
workspace,
workspacePath,
options: config,
tree: host,
project
});
}
return setupStaticDeployment({
workspace,
workspacePath,
options: config,
tree: host,
project
});
};
};

export const ngAddSetupProject = (
options: DeployOptions & { isUniversalProject: boolean }
options: DeployOptions
) => async (host: Tree) => {
const projects = await listProjects();
const { firebaseProject } = await projectPrompt(projects);
return setupProject(host, {...options, firebaseProject});
const isUniversalProject = (global as any).setupAsAngularUniversalApp;
return setupProject(host, {...options, firebaseProject, isUniversalProject });
};

export const ngAdd = (options: DeployOptions) => (
host: Tree,
context: SchematicContext
) => {

addFirebaseHostingDependencies(host, context);

const {project} = getProject(options, host);

// In Angular 12 it appears I might need some sort of timeout to allow
// node_modules to resolve?
const timeout = new Promise(resolve => setTimeout(resolve, 1_000));

return timeout.
then(() => projectTypePrompt(project)).
then(({ universalProject }: { universalProject: boolean }) => {
if (universalProject) {
addFirebaseFunctionsDependencies(host, context);
}
const projectOptions: DeployOptions & { isUniversalProject: boolean } = {
...options,
isUniversalProject: universalProject
};
context.addTask(new RunSchematicTask('ng-add-setup-project', projectOptions), [
context.addTask(new NodePackageInstallTask())
]);
}
);
};
export const ngAdd = (options: DeployOptions) => chain([
addFirebaseHostingDependencies(),
addFirebaseFunctionsDependencies(options),
ngAddSetupProject(options),
]);
60 changes: 58 additions & 2 deletions src/schematics/utils.ts
@@ -1,7 +1,9 @@
import { readFileSync } from 'fs';
import { FirebaseRc, Project, WorkspaceProject } from './interfaces';
import { FirebaseRc, Project, Workspace, WorkspaceProject } from './interfaces';
import { join } from 'path';
import { isUniversalApp } from './ng-add-ssr';
import { SchematicsException, Tree } from '@angular-devkit/schematics';
import { DeployOptions } from './ng-add-common';

export async function listProjects() {
const firebase = require('firebase-tools');
Expand Down Expand Up @@ -41,6 +43,60 @@ const searchProjects = (projects: Project[]) => {
};
};


export function getWorkspace(
host: Tree
): { path: string; workspace: Workspace } {
const possibleFiles = ['/angular.json', '/.angular.json'];
const path = possibleFiles.filter(p => host.exists(p))[0];

const configBuffer = host.read(path);
if (configBuffer === null) {
throw new SchematicsException(`Could not find angular.json`);
}

// We can not depend on this library to have be included in older (or newer) Angular versions.
// Require here, since the schematic will add it to the package.json and install it before
// continuing.
const { parse }: typeof import('jsonc-parser') = require('jsonc-parser');

const workspace = parse(configBuffer.toString()) as Workspace|undefined;
if (!workspace) {
throw new SchematicsException('Could not parse angular.json');
}

return {
path,
workspace
};
}

export const getProject = (options: DeployOptions, host: Tree) => {
const { workspace } = getWorkspace(host);
const projectName = options.project || workspace.defaultProject;

if (!projectName) {
throw new SchematicsException(
'No Angular project selected and no default project in the workspace'
);
}

const project = workspace.projects[projectName];
if (!project) {
throw new SchematicsException(
'The specified Angular project is not defined in this workspace'
);
}

if (project.projectType !== 'application') {
throw new SchematicsException(
`Deploy requires an Angular project type of "application" in angular.json`
);
}

return {project, projectName};
};

export const projectPrompt = (projects: Project[]) => {
const inquirer = require('inquirer');
inquirer.registerPrompt(
Expand All @@ -55,7 +111,7 @@ export const projectPrompt = (projects: Project[]) => {
});
};

export const projectTypePrompt = (project: WorkspaceProject) => {
export const projectTypePrompt = (project: WorkspaceProject): Promise<{ universalProject: boolean }> => {
if (isUniversalApp(project)) {
return require('inquirer').prompt({
type: 'confirm',
Expand Down

0 comments on commit dbda308

Please sign in to comment.