Skip to content

Commit

Permalink
Issue #SB-28710 fix: Publishing as NG module - sunbird-epub-player
Browse files Browse the repository at this point in the history
  • Loading branch information
vaibhavbhuva committed Mar 14, 2022
1 parent 066b9c6 commit eed89d1
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 3 deletions.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
"test": "ng test sunbird-epub-player --code-coverage --watch=false",
"lint": "ng lint",
"e2e": "ng e2e",
"build-lib": "ng build sunbird-epub-player && node assets-copy.js",
"build-lib": "ng build sunbird-epub-player && node assets-copy.js && npm run schematics:build",
"link-player": "cd dist/sunbird-epub-player && npm link && cd ../.. && npm link @project-sunbird/sunbird-epub-player-v9",
"build-lib-link": "npm run build-lib && npm run link-player",
"lib-test": "ng test sunbird-epub-player",
"mybuild": "npm run build",
"serve": "node assets-copy.js && npm link ./dist/sunbird-epub-player && ng serve",
"build-web-component": "npm run build-lib-link && ng build epub-player-wc --output-hashing none && node ./build-wc.js"
"build-web-component": "npm run build-lib-link && ng build epub-player-wc --output-hashing none && node ./build-wc.js",
"schematics:build": "./node_modules/.bin/tsc -p tsconfig.schematics.json",
"postschematics:build": "./node_modules/.bin/copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ./dist/sunbird-epub-player/"
},
"private": true,
"dependencies": {
Expand Down Expand Up @@ -49,6 +51,7 @@
"@types/node": "^8.9.5",
"codelyzer": "^5.0.0",
"concat": "^1.0.3",
"copyfiles": "^2.4.1",
"fs-extra": "^10.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
Expand Down
6 changes: 5 additions & 1 deletion projects/sunbird-epub-player/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,9 @@
"sunbird epub player",
"project-sunbird"
],
"license": "MIT"
"license": "MIT",
"schematics": "./schematics/collection.json",
"ng-add": {
"save": "devDependencies"
}
}
18 changes: 18 additions & 0 deletions schematics/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Add @project-sunbird/sunbird-epub-player-v9 to the project.",
"factory": "./ng-add/index",
"schema": "./ng-add/schema.json",
"aliases": ["install"]
},
"ng-add-setup-project": {
"private": true,
"description": "Sets up the specified project after the ng-add dependencies have been installed.",
"factory": "./ng-add/setup-project",
"schema": "./ng-add/schema.json"
}
}
}

53 changes: 53 additions & 0 deletions schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics';
import {
NodePackageInstallTask,
RunSchematicTask,
} from '@angular-devkit/schematics/tasks';

import {getWorkspace} from '@schematics/angular/utility/workspace';

import {Schema} from './schema';
import * as messages from './messages';
import {addPackageToPackageJson} from '../utils/package-config';

interface VersionOptions {
[key: string]: string;
}

const VERSIONS: VersionOptions = {
// automatically filled from package.json during the build
'@project-sunbird/sb-styles': '0.0.7',
'@project-sunbird/client-services': '^3.4.8',
epubjs: '0.3.88',
};

/**
* This is executed when `ng add @project-sunbird/sunbird-epub-player-v9` is run.
* It installs all dependencies in the 'package.json' and runs 'ng-add-setup-project' schematic.
*/
export default function ngAdd(options: Schema): Rule {
return async (tree: Tree, context: SchematicContext) => {

// Checking that project exists
const {project} = options;
if (project) {
const workspace = await getWorkspace(tree);
const projectWorkspace = workspace.projects.get(project);

if (!projectWorkspace) {
throw new SchematicsException(messages.noProject(project));
}
}

// Installing dependencies
for (const key in VERSIONS) {
if (VERSIONS.hasOwnProperty(key)) {
addPackageToPackageJson(tree, key, VERSIONS[key]);
}
}

context.addTask(new RunSchematicTask('ng-add-setup-project', options), [
context.addTask(new NodePackageInstallTask()),
]);
};
}
11 changes: 11 additions & 0 deletions schematics/ng-add/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export function noProject(project: string) {
return `Unable to find project '${project}' in the workspace`;
}

export function noMainFile(projectName: string) {
return `Unable to find 'build.options.main' file path for project "${projectName}"`;
}

export function noModuleFile(moduleFilePath: string) {
return `File '${moduleFilePath}' does not exist.`;
}
16 changes: 16 additions & 0 deletions schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "sunbird-epub-player-ng-add",
"title": "Sunbird Epub Player ng-add schematic",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "Name of the project.",
"$default": {
"$source": "projectName"
}
}
},
"required": []
}
6 changes: 6 additions & 0 deletions schematics/ng-add/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Schema {
/**
* Name of the project where sunbird-epub-player library should be installed
*/
project?: string;
}
14 changes: 14 additions & 0 deletions schematics/ng-add/setup-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {chain, Rule} from '@angular-devkit/schematics';
import {Schema} from './schema';
import {addPlayerModuleToAppModule} from './steps/add-player-module';
import { addPlayerStyles } from './steps/add-player-style';
/**
* Sets up a project with all required to run sunbird pdf player.
* This is run after 'package.json' was patched and all dependencies installed
*/
export default function ngAddSetupProject(options: Schema): Rule {
return chain([
addPlayerModuleToAppModule(options),
addPlayerStyles(options),
]);
}
75 changes: 75 additions & 0 deletions schematics/ng-add/steps/add-player-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics';
import {getAppModulePath} from '@schematics/angular/utility/ng-ast-utils';
import {addImportToModule} from '@schematics/angular/utility/ast-utils';
import {InsertChange} from '@schematics/angular/utility/change';
import * as ts from '@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript';
import {Schema} from '../schema';
import {getWorkspace} from '@schematics/angular/utility/workspace';
import * as messages from '../messages';
import { getProjectTargetOptions } from '../../utils/project';

const MODULE_NAME = 'SunbirdEpubPlayerModule';
const PACKAGE_NAME = '@project-sunbird/sunbird-epub-player-v9';

/**
* Patches main application module by adding 'SunbirdEpubPlayerModule' import.
*
* Relevant 'angular.json' structure is:
*
* {
* "projects" : {
* "projectName": {
* "architect": {
* "build": {
* "options": {
* "main": "src/main.ts"
* }
* }
* }
* }
* },
* "defaultProject": "projectName"
* }
*
*/
export function addPlayerModuleToAppModule(options: Schema): Rule {
return async (host: Tree) => {
const workspace = await getWorkspace(host);
const projectName = options.project || (workspace.extensions.defaultProject as string);

// 1. getting project by name
const project: any = workspace.projects.get(projectName);
if (!project) {
throw new SchematicsException(messages.noProject(projectName));
}

// 2. getting main file for project
const projectBuildOptions = getProjectTargetOptions(project, 'build');
const mainFilePath = projectBuildOptions.main as string;
if (!mainFilePath || !host.read(mainFilePath)) {
throw new SchematicsException(messages.noMainFile(projectName));
}

// 3. getting main app module file
const appModuleFilePath = getAppModulePath(host, mainFilePath);
const appModuleFileText = host.read(appModuleFilePath);
if (appModuleFileText === null) {
throw new SchematicsException(messages.noModuleFile(appModuleFilePath));
}

// 4. adding `NgbModule` to the app module
const appModuleSource =
ts.createSourceFile(appModuleFilePath, appModuleFileText.toString('utf-8'), ts.ScriptTarget.Latest, true);

const changes =
addImportToModule(appModuleSource, appModuleFilePath, MODULE_NAME, PACKAGE_NAME);

const recorder = host.beginUpdate(appModuleFilePath);
for (const change of changes) {
if (change instanceof InsertChange) {
recorder.insertLeft(change.pos, change.toAdd);
}
}
host.commitUpdate(recorder);
};
}
94 changes: 94 additions & 0 deletions schematics/ng-add/steps/add-player-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Rule, Tree, SchematicsException } from '@angular-devkit/schematics';
import { Schema } from '../schema';
import * as messages from '../messages';
import { getProjectTargetOptions } from '../../utils/project';
import { getWorkspace, updateWorkspace } from '@schematics/angular/utility/workspace';
import { workspaces, JsonArray } from '@angular-devkit/core';

// Default styles, assets and script

const SB_STYLES = [
'node_modules/@project-sunbird/sb-styles/assets/_styles.scss'
];
const SB_ASSETS = [{
glob: '**/*.*',
input: './node_modules/@project-sunbird/sunbird-epub-player-v9/lib/assets/',
output: '/assets/'
}];
const SB_SCRIPTS = [
'node_modules/epubjs/dist/epub.js'
];


/**
* we're simply adding styles to the 'angular.json'
*/
export function addPlayerStyles(options: Schema): Rule {
return async (host: Tree) => {
const workspace: any = await getWorkspace(host);

const projectName = options.project || (workspace.extensions.defaultProject as string);
const project = workspace.projects.get(projectName);
if (!project) {
throw new SchematicsException(messages.noProject(projectName));
}
// just patching 'angular.json'
return addPlayerToAngularJson(workspace, project);
};
}

/**
* Patches 'angular.json' to add styles
*/
function addPlayerToAngularJson(
workspace: any,
project: workspaces.ProjectDefinition
): Rule {
const targetOptions = getProjectTargetOptions(project, 'build');
addStyleToTarget(targetOptions, SB_STYLES);
addAssetsToTarget(targetOptions, SB_ASSETS);
addScriptToTarget(targetOptions, SB_SCRIPTS);
return updateWorkspace(workspace);
}

function addStyleToTarget(targetOptions: any, assetPaths: Array<any>) {
const styles = (targetOptions.styles as JsonArray | undefined);
if (!styles) {
targetOptions.styles = assetPaths;
} else {
const existingStyles: any = styles.map((s: any) => typeof s === 'string' ? s : s.input);
assetPaths.forEach((style: any) => {
if (!existingStyles.includes(typeof style === 'string' ? style : style.input)) {
styles.unshift(style);
}
});
}
}

function addAssetsToTarget(targetOptions: any, assetPaths: Array<any>) {
const assets = (targetOptions.assets as JsonArray | undefined);
if (!assets) {
targetOptions.assets = assetPaths;
} else {
const existingAssets: any = assets.map((s: any) => typeof s === 'string' ? s : s.input);
assetPaths.forEach((asset: any) => {
if (!existingAssets.includes(typeof asset === 'string' ? asset : asset.input)) {
assets.unshift(asset);
}
});
}
}

function addScriptToTarget(targetOptions: any, assetPaths: Array<any>) {
const scripts = (targetOptions.scripts as JsonArray | undefined);
if (!scripts) {
targetOptions.scripts = assetPaths;
} else {
const existingScripts: any = scripts.map((s: any) => typeof s === 'string' ? s : s.input);
assetPaths.forEach((script: any) => {
if (!existingScripts.includes(typeof script === 'string' ? script : script.input)) {
scripts.unshift(script);
}
});
}
}
64 changes: 64 additions & 0 deletions schematics/utils/package-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {Tree} from '@angular-devkit/schematics';

interface PackageJson {
dependencies: Record<string, string>;
}

/**
* Sorts the keys of the given object.
* @returns A new object instance with sorted keys
*/
function sortObjectByKeys(obj: Record<string, string>) {
return Object.keys(obj)
.sort()
.reduce((result, key) => {
result[key] = obj[key];
return result;
}, {} as Record<string, string>);
}

/** Adds a package to the package.json in the given host tree. */
export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {
if (host.exists('package.json')) {
// tslint:disable-next-line:no-non-null-assertion
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText) as PackageJson;

if (!json.dependencies) {
json.dependencies = {};
}

if (!json.dependencies[pkg]) {
json.dependencies[pkg] = version;
json.dependencies = sortObjectByKeys(json.dependencies);
}

host.overwrite('package.json', JSON.stringify(json, null, 2));
}

return host;
}

/** Gets the version of the specified package by looking at the package.json in the given tree. */
export function getPackageVersionFromPackageJson(tree: Tree, name: string): string | null {
if (!tree.exists('package.json')) {
return null;
}

// tslint:disable-next-line:no-non-null-assertion
const packageJson = JSON.parse(tree.read('package.json')!.toString('utf8')) as PackageJson;

if (packageJson.dependencies && packageJson.dependencies[name]) {
return packageJson.dependencies[name];
}

return null;
}
Loading

0 comments on commit eed89d1

Please sign in to comment.