[ACA-1646] "dev tools" extension (#567)

* dev tools extension project

* code editor integration

* latest editor, offline setup

* override extension config (session only)

* schema support

* wire external plugins with experimental flag

* update package scripts

* sidebar extensions scaffold

* propagate extension tabs to info drawer

* separate tab components for info drawer

* extensibility for info drawer

* support tab icons
This commit is contained in:
Denys Vuika 2018-08-16 16:55:06 +01:00 committed by GitHub
parent ed23a21a00
commit 89aec91caf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2391 additions and 149 deletions

View File

@ -5,5 +5,5 @@ LABEL maintainer="Denys Vuika <denys.vuika@alfresco.com>"
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
COPY dist/ .
COPY dist/app/ .

View File

@ -11,7 +11,7 @@
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"outputPath": "dist/app",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
@ -45,6 +45,16 @@
"glob": "pdf.worker.js",
"input": "node_modules/pdfjs-dist/build",
"output": "/"
},
{
"glob": "**/*",
"input": "node_modules/monaco-editor/min",
"output": "./assets/monaco"
},
{
"glob": "**/*.js",
"input": "node_modules/@ngstack/code-editor/workers",
"output": "./assets/workers"
}
],
"styles": [
@ -106,9 +116,9 @@
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"stylePreprocessorOptions": {
"includePaths": [
"src/app/ui"
]
"includePaths": [
"src/app/ui"
]
},
"tsConfig": "src/tsconfig.spec.json",
"scripts": [
@ -188,6 +198,46 @@
}
}
}
},
"aca-dev-tools": {
"root": "projects/aca-dev-tools",
"sourceRoot": "projects/aca-dev-tools/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/aca-dev-tools/tsconfig.lib.json",
"project": "projects/aca-dev-tools/ng-package.json"
},
"configurations": {
"production": {
"project": "projects/aca-dev-tools/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/aca-dev-tools/src/test.ts",
"tsConfig": "projects/aca-dev-tools/tsconfig.spec.json",
"karmaConfig": "projects/aca-dev-tools/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/aca-dev-tools/tsconfig.lib.json",
"projects/aca-dev-tools/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "alfresco-content-app",

View File

@ -228,6 +228,46 @@
"type": "boolean"
}
}
},
"sidebarTabRef": {
"type": "object",
"required": ["id", "component"],
"properties": {
"id": {
"description": "Unique identifier for the navigation group",
"type": "string"
},
"title": {
"description": "Element title",
"type": "string"
},
"component": {
"description": "Component id",
"type": "string"
},
"icon": {
"description": "Material icon name",
"type": "string"
},
"disabled": {
"description": "Toggles disabled state",
"type": "boolean"
},
"order": {
"description": "Element order",
"type": "number"
},
"rules": {
"description": "Element rules",
"type": "object",
"properties": {
"visible": {
"description": "Rule to evaluate the visibility state",
"type": "string"
}
}
}
}
}
},
@ -306,6 +346,12 @@
"items": { "$ref": "#/definitions/navBarGroupRef" },
"minItems": 1
},
"sidebar": {
"description": "Sidebar extensions",
"type": "array",
"items": { "$ref": "#/definitions/sidebarTabRef" },
"minItems": 1
},
"content": {
"description": "Main application content extensions",
"type": "object",

1101
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,12 @@
"license": "LGPL-3.0",
"scripts": {
"ng": "ng",
"start": "npm run server-versions && ng serve --open",
"start": "npm run server-versions && ng build aca-dev-tools && ng serve --open",
"start:prod": "npm run server-versions && ng serve --prod --open",
"build": "npm run server-versions && ng build --prod",
"build:prod": "npm run server-versions && ng build --prod",
"build:dev": "npm run server-versions && ng build",
"build:tomcat": "npm run server-versions && ng build --base-href ./",
"build:electron": "npm run server-versions && ng build --output-path www --base-href ./",
"test": "ng test --code-coverage",
"test:ci": "ng test --code-coverage --watch=false && cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage && rm -rf ./coverage",
"build": "npm run server-versions && ng build aca-dev-tools --prod && ng build --prod",
"build:dev": "npm run server-versions && ng build aca-dev-tools && ng build",
"test": "ng test alfresco-content-app --code-coverage",
"test:ci": "ng test alfresco-content-app --code-coverage --watch=false && cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage && rm -rf ./coverage",
"lint": "ng lint",
"server-versions": "rimraf ./src/versions.json && npm list --depth=0 --json=true --prod=true > ./src/versions.json || exit 0",
"_e2e": "ng e2e",
@ -21,7 +18,7 @@
"start:docker": "docker-compose up -d --build && wait-on http://localhost:8080 && wait-on http://localhost:4000",
"stop:docker": "docker-compose stop",
"e2e:docker": "npm run start:docker && npm run e2e && npm run stop:docker",
"spellcheck": "cspell 'src/**/*.ts' 'e2e/**/*.ts'"
"spellcheck": "cspell 'src/**/*.ts' 'e2e/**/*.ts' 'projects/**/*.ts'"
},
"private": true,
"dependencies": {
@ -46,6 +43,7 @@
"@ngrx/router-store": "^6.1.0",
"@ngrx/store": "^6.1.0",
"@ngrx/store-devtools": "^6.1.0",
"@ngstack/code-editor": "^0.4.3",
"@ngx-translate/core": "^10.0.2",
"alfresco-js-api": "2.5.0",
"core-js": "^2.5.7",
@ -53,6 +51,7 @@
"minimatch-browser": "^1.0.0",
"moment": "^2.22.2",
"moment-es6": "1.0.0",
"monaco-editor": "^0.14.2",
"pdfjs-dist": "2.0.303",
"rxjs": "^6.2.2",
"web-animations-js": "2.3.1",
@ -60,6 +59,7 @@
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.7.0",
"@angular-devkit/build-ng-packagr": "~0.7.0",
"@angular/cli": "^6.1.3",
"@angular/compiler-cli": "6.1.2",
"@angular/language-service": "6.1.2",
@ -82,10 +82,13 @@
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"ng-packagr": "^3.0.0",
"protractor": "5.3.2",
"rimraf": "2.6.2",
"selenium-webdriver": "4.0.0-alpha.1",
"ts-node": "~4.1.0",
"tsickle": ">=0.29.0",
"tslib": "^1.9.0",
"tslint": "~5.11.0",
"typescript": "^2.9.2",
"wait-on": "2.1.0"

View File

@ -0,0 +1,31 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@ -0,0 +1,8 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/aca-dev-tools",
"deleteDestPath": false,
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/aca-dev-tools",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@ -0,0 +1,9 @@
{
"name": "aca-dev-tools",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^6.0.0",
"@angular/core": "^6.0.0",
"@ngstack/code-editor": "^0.4.1"
}
}

View File

@ -0,0 +1,39 @@
<div class="inner-layout">
<div class="inner-layout__header">
<adf-breadcrumb root="app.extensions.json"></adf-breadcrumb>
<adf-toolbar class="inline">
<button
mat-icon-button
color="primary"
title="Save changes"
(click)="saveChanges()">
<mat-icon>save_alt</mat-icon>
</button>
<button
mat-icon-button
color="primary"
title="Revert changes"
(click)="revertChanges()">
<mat-icon>history</mat-icon>
</button>
</adf-toolbar>
</div>
<div class="inner-layout__content">
<div class="inner-layout__panel">
<div fxLayout="column" fxFill>
<div fxLayout fxFlex>
<div fxFlex="100">
<ng-container *ngIf="model">
<ngs-code-editor
[codeModel]="model"
(valueChanged)="onCodeChanged($event)">
</ngs-code-editor>
</ng-container>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
.lib-aca-dev-tools {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
overflow: hidden;
min-height: 0;
}
.ngs-code-editor {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
max-height: 100%;
overflow: hidden;
min-height: 0;
.editor {
// border: 1px solid grey;
// min-height: 400px;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AcaDevToolsComponent } from './aca-dev-tools.component';
describe('AcaDevToolsComponent', () => {
let component: AcaDevToolsComponent;
let fixture: ComponentFixture<AcaDevToolsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AcaDevToolsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AcaDevToolsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,72 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { CodeModel } from '@ngstack/code-editor';
import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { forkJoin } from 'rxjs';
@Component({
selector: 'lib-aca-dev-tools',
encapsulation: ViewEncapsulation.None,
host: { class: 'lib-aca-dev-tools' },
templateUrl: './aca-dev-tools.component.html',
styleUrls: ['./aca-dev-tools.component.scss']
})
export class AcaDevToolsComponent implements OnInit {
model: CodeModel = null;
private code: string;
constructor(private route: ActivatedRoute, private http: HttpClient) {}
ngOnInit() {
const routeData = this.route.snapshot.data;
if (!routeData) {
return;
}
const schemaUri = routeData.schemaUri;
const getSchema = this.http.get(routeData.schemaPath);
const getConfig = this.http.get(routeData.configPath, { responseType: 'text' });
forkJoin([getSchema, getConfig]).subscribe(
([schema, config]) => {
let code = config;
const override = sessionStorage.getItem('aca.extension.config');
if (override) {
code = override;
}
this.model = {
language: 'json',
uri: 'app.extensions.json',
value: code,
schemas: [
{
uri: schemaUri,
schema
}
]
};
this.code = code;
},
err => {
console.log(err);
}
);
}
onCodeChanged(value: string) {
this.code = value;
}
saveChanges() {
sessionStorage.setItem('aca.extension.config', this.code);
// window.location.reload(true);
}
revertChanges() {
sessionStorage.removeItem('aca.extension.config');
window.location.reload(true);
}
}

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CodeEditorModule } from '@ngstack/code-editor';
import { FlexLayoutModule } from '@angular/flex-layout';
import { AcaDevToolsComponent } from './aca-dev-tools.component';
import { CoreModule } from '@alfresco/adf-core';
import { ContentModule } from '@alfresco/adf-content-services';
@NgModule({
imports: [
FlexLayoutModule,
CodeEditorModule.forChild(),
CoreModule.forChild(),
ContentModule.forChild()
],
declarations: [AcaDevToolsComponent],
exports: [AcaDevToolsComponent],
entryComponents: [AcaDevToolsComponent]
})
export class AcaDevToolsModule {}

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { AcaDevToolsService } from './aca-dev-tools.service';
describe('AcaDevToolsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AcaDevToolsService]
});
});
it('should be created', inject([AcaDevToolsService], (service: AcaDevToolsService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AcaDevToolsService {
constructor() { }
}

View File

@ -0,0 +1,7 @@
/*
* Public API Surface of aca-dev-tools
*/
export * from './lib/aca-dev-tools.service';
export * from './lib/aca-dev-tools.component';
export * from './lib/aca-dev-tools.module';

View File

@ -0,0 +1,22 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -0,0 +1,33 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2015"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"flatModuleId": "AUTOGENERATED",
"flatModuleOutFile": "AUTOGENERATED"
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

View File

@ -13,7 +13,8 @@
"cardview": false,
"permissions": false,
"share": false,
"extensions": false
"extensions": false,
"external-plugins": false
},
"headerColor": "#2196F3",
"languagePicker": false,

View File

@ -88,7 +88,7 @@ import { ContextMenuModule } from './components/context-menu/context-menu.module
}),
MaterialModule,
CoreModule.forRoot(),
ContentModule,
ContentModule.forRoot(),
AppStoreModule,
CoreExtensionsModule.forRoot(),
ExtensionsModule,

View File

@ -90,7 +90,7 @@ export const APP_ROUTES: Routes = [
},
{
path: 'preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,
@ -120,7 +120,7 @@ export const APP_ROUTES: Routes = [
},
{
path: ':folderId/preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,
@ -152,7 +152,7 @@ export const APP_ROUTES: Routes = [
},
{
path: 'preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,
@ -161,7 +161,7 @@ export const APP_ROUTES: Routes = [
},
{
path: ':folderId/preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,
@ -185,7 +185,7 @@ export const APP_ROUTES: Routes = [
},
{
path: 'preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,
@ -209,7 +209,7 @@ export const APP_ROUTES: Routes = [
},
{
path: 'preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,
@ -228,7 +228,7 @@ export const APP_ROUTES: Routes = [
},
{
path: 'about',
loadChildren: 'app/components/about/about.module#AboutModule',
loadChildren: 'src/app/components/about/about.module#AboutModule',
data: {
title: 'APP.BROWSE.ABOUT.TITLE'
}
@ -246,7 +246,7 @@ export const APP_ROUTES: Routes = [
},
{
path: 'preview/:nodeId',
loadChildren: 'app/components/preview/preview.module#PreviewModule',
loadChildren: 'src/app/components/preview/preview.module#PreviewModule',
data: {
title: 'APP.PREVIEW.TITLE',
navigateMultiple: true,

View File

@ -0,0 +1,38 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Input } from '@angular/core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
@Component({
selector: 'app-comments-tab',
template: `
<adf-comments [nodeId]="node?.id"></adf-comments>
`
})
export class CommentsTabComponent {
@Input()
node: MinimalNodeEntryEntity;
}

View File

@ -0,0 +1,94 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import {
Component,
Input,
OnInit,
OnDestroy,
ViewChild,
ViewContainerRef,
ComponentRef,
ComponentFactoryResolver,
OnChanges,
SimpleChanges
} from '@angular/core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { ExtensionService } from '../../../extensions/extension.service';
@Component({
selector: 'app-dynamic-tab',
template: `<div #content></div>`
})
export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy {
@ViewChild('content', { read: ViewContainerRef })
content: ViewContainerRef;
@Input()
id: string;
@Input()
node: MinimalNodeEntryEntity;
private componentRef: ComponentRef<any>;
constructor(
private extensions: ExtensionService,
private componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnInit() {
const componentType = this.extensions.getComponentById(this.id);
if (componentType) {
const factory = this.componentFactoryResolver.resolveComponentFactory(
componentType
);
if (factory) {
this.content.clear();
this.componentRef = this.content.createComponent(factory, 0);
this.updateInstance();
}
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes.node) {
this.updateInstance();
}
}
ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
this.componentRef = null;
}
}
private updateInstance() {
if (this.componentRef && this.componentRef.instance) {
this.componentRef.instance.node = this.node;
}
}
}

View File

@ -3,38 +3,12 @@
</div>
<ng-container *ngIf="!isLoading && !!displayNode">
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate">
<adf-content-metadata-card
[readOnly]="!canUpdateNode"
[displayEmpty]="canUpdateNode"
[preset]="'custom'"
[node]="displayNode">
</adf-content-metadata-card>
<adf-info-drawer-tab
*ngFor="let tab of tabs"
[icon]="tab.icon"
[label]="tab.title | translate">
<app-dynamic-tab [node]="displayNode" [id]="tab.component">
</app-dynamic-tab>
</adf-info-drawer-tab>
<ng-container *ifExperimental="'comments'; else versions">
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.COMMENTS' | translate">
<adf-comments [nodeId]="displayNode.id"></adf-comments>
</adf-info-drawer-tab>
</ng-container>
<ng-template #versions>
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.VERSIONS' | translate">
<ng-container *ngIf="isFileSelected;else empty">
<adf-version-manager
[showComments]="'adf-version-manager.allowComments' | adfAppConfig:true"
[allowDownload]="'adf-version-manager.allowDownload' | adfAppConfig:true"
[node]="displayNode">
</adf-version-manager>
</ng-container>
<ng-template #empty>
<div class="adf-manage-versions-empty">
<mat-icon class="adf-manage-versions-empty-icon">face</mat-icon>
{{ 'VERSION.SELECTION.EMPTY' | translate }}
</div>
</ng-template>
</adf-info-drawer-tab>
</ng-template>
</adf-info-drawer>
</ng-container>

View File

@ -23,41 +23,34 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { NodePermissionService } from '../../services/node-permission.service';
import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service';
import { SidebarTabRef } from '../../extensions/sidebar.extensions';
@Component({
selector: 'aca-info-drawer',
templateUrl: './info-drawer.component.html'
})
export class InfoDrawerComponent implements OnChanges {
export class InfoDrawerComponent implements OnChanges, OnInit {
@Input() nodeId: string;
@Input() node: MinimalNodeEntity;
isLoading = false;
displayNode: MinimalNodeEntryEntity;
canUpdateNode = false;
get isFileSelected(): boolean {
if (this.node && this.node.entry) {
// workaround for shared files type.
if (this.node.entry.nodeId) {
return true;
} else {
return this.node.entry.isFile;
}
}
return false;
}
tabs: Array<SidebarTabRef> = [];
constructor(
public permission: NodePermissionService,
private contentApi: ContentApiService
private contentApi: ContentApiService,
private extensions: ExtensionService
) {}
ngOnChanges(changes: SimpleChanges) {
ngOnInit() {
this.tabs = this.extensions.getSidebarTabs();
}
ngOnChanges() {
if (this.node) {
const entry = this.node.entry;
if (entry.nodeId) {
@ -103,6 +96,5 @@ export class InfoDrawerComponent implements OnChanges {
private setDisplayNode(node: MinimalNodeEntryEntity) {
this.displayNode = node;
this.canUpdateNode = node && this.permission.check(node, ['update']);
}
}

View File

@ -30,6 +30,20 @@ import { ContentMetadataModule, VersionManagerModule } from '@alfresco/adf-conte
import { InfoDrawerComponent } from './info-drawer.component';
import { DirectivesModule } from '../../directives/directives.module';
import { MaterialModule } from '../../material.module';
import { MetadataTabComponent } from './metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from './comments-tab/comments-tab.component';
import { VersionsTabComponent } from './versions-tab/versions-tab.component';
import { DynamicTabComponent } from './dynamic-tab/dynamic-tab.component';
export function components() {
return [
InfoDrawerComponent,
MetadataTabComponent,
CommentsTabComponent,
VersionsTabComponent,
DynamicTabComponent
];
}
@NgModule({
imports: [
@ -41,10 +55,13 @@ import { MaterialModule } from '../../material.module';
DirectivesModule
],
declarations: [
InfoDrawerComponent
...components()
],
exports: [
InfoDrawerComponent
...components()
],
entryComponents: [
...components()
]
})
export class AppInfoDrawerModule {}

View File

@ -0,0 +1,55 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Input, OnChanges, ViewEncapsulation } from '@angular/core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { NodePermissionService } from '../../../services/node-permission.service';
@Component({
selector: 'app-metadata-tab',
template: `
<adf-content-metadata-card
[readOnly]="!canUpdateNode"
[displayEmpty]="canUpdateNode"
[preset]="'custom'"
[node]="node">
</adf-content-metadata-card>
`,
encapsulation: ViewEncapsulation.None,
host: { 'class': 'app-metadata-tab' }
})
export class MetadataTabComponent implements OnChanges {
@Input()
node: MinimalNodeEntryEntity;
canUpdateNode = false;
constructor(public permission: NodePermissionService) {}
ngOnChanges() {
this.canUpdateNode =
this.node && this.permission.check(this.node, ['update']);
}
}

View File

@ -0,0 +1,70 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
@Component({
selector: 'app-versions-tab',
template: `
<ng-container *ngIf="isFileSelected;else empty">
<adf-version-manager
[showComments]="'adf-version-manager.allowComments' | adfAppConfig:true"
[allowDownload]="'adf-version-manager.allowDownload' | adfAppConfig:true"
[node]="node">
</adf-version-manager>
</ng-container>
<ng-template #empty>
<div class="adf-manage-versions-empty">
<mat-icon class="adf-manage-versions-empty-icon">face</mat-icon>
{{ 'VERSION.SELECTION.EMPTY' | translate }}
</div>
</ng-template>
`
})
export class VersionsTabComponent implements OnInit, OnChanges {
@Input()
node: MinimalNodeEntryEntity;
isFileSelected = false;
ngOnInit() {
this.updateState();
}
ngOnChanges() {
this.updateState();
}
private updateState() {
if (this.node && this.node.nodeId) {
// workaround for shared files type.
this.isFileSelected = true;
} else {
this.isFileSelected = this.node.isFile;
}
}
}

View File

@ -1,8 +1,26 @@
import { NgModule } from '@angular/core';
import { AcaDevToolsModule, AcaDevToolsComponent } from 'aca-dev-tools';
import { ExtensionService } from './extensions/extension.service';
import { CodeEditorModule } from '@ngstack/code-editor';
// Main entry point for external extensions only.
// For any application-specific code use CoreExtensionsModule instead.
@NgModule({
imports: []
imports: [
CodeEditorModule.forRoot({
// use local Monaco installation
baseUrl: 'assets/monaco',
// use local Typings Worker
typingsWorkerUrl: 'assets/workers/typings-worker.js'
}),
AcaDevToolsModule
]
})
export class ExtensionsModule {}
export class ExtensionsModule {
constructor(extensions: ExtensionService) {
extensions.setComponents({
'app.dev.tools.component': AcaDevToolsComponent
});
}
}

View File

@ -36,11 +36,17 @@ import { CustomExtensionComponent } from './components/custom-component/custom.c
import { ToggleInfoDrawerComponent } from '../components/toolbar/toggle-info-drawer/toggle-info-drawer.component';
import { ToggleFavoriteComponent } from '../components/toolbar/toggle-favorite/toggle-favorite.component';
import { ToolbarButtonComponent } from './components/toolbar/toolbar-button.component';
import { MetadataTabComponent } from '../components/info-drawer/metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from '../components/info-drawer/comments-tab/comments-tab.component';
import { VersionsTabComponent } from '../components/info-drawer/versions-tab/versions-tab.component';
export function setupExtensions(extensions: ExtensionService): Function {
extensions.setComponents({
'app.layout.main': LayoutComponent,
'app.components.trashcan': TrashcanComponent,
'app.components.tabs.metadata': MetadataTabComponent,
'app.components.tabs.comments': CommentsTabComponent,
'app.components.tabs.versions': VersionsTabComponent,
'app.toolbar.toggleInfoDrawer': ToggleInfoDrawerComponent,
'app.toolbar.toggleFavorite': ToggleFavoriteComponent
});

View File

@ -27,6 +27,7 @@ import { NavBarGroupRef } from './navbar.extensions';
import { RouteRef } from './routing.extensions';
import { RuleRef } from './rule.extensions';
import { ActionRef, ContentActionRef } from './action.extensions';
import { SidebarTabRef } from './sidebar.extensions';
export interface ExtensionConfig {
$name: string;
@ -44,6 +45,7 @@ export interface ExtensionConfig {
actions?: Array<ContentActionRef>;
};
navbar?: Array<NavBarGroupRef>;
sidebar?: Array<SidebarTabRef>;
content?: {
actions?: Array<ContentActionRef>;
contextActions?: Array<ContentActionRef>

View File

@ -37,6 +37,7 @@ import { RuleContext, RuleRef, RuleEvaluator, RuleParameter } from './rule.exten
import { ActionRef, ContentActionRef, ContentActionType } from './action.extensions';
import * as core from './evaluators/core.evaluators';
import { NodePermissionService } from '../services/node-permission.service';
import { SidebarTabRef } from './sidebar.extensions';
@Injectable()
export class ExtensionService implements RuleContext {
@ -58,6 +59,7 @@ export class ExtensionService implements RuleContext {
openWithActions: Array<ContentActionRef> = [];
createActions: Array<ContentActionRef> = [];
navbar: Array<NavBarGroupRef> = [];
sidebar: Array<SidebarTabRef> = [];
authGuards: { [key: string]: Type<{}> } = {};
components: { [key: string]: Type<{}> } = {};
@ -88,7 +90,15 @@ export class ExtensionService implements RuleContext {
this.loadConfig(this.configPath, 0).then(result => {
let config = result.config;
if (config.$references && config.$references.length > 0) {
const override = sessionStorage.getItem('aca.extension.config');
if (override) {
console.log('overriding extension config');
config = JSON.parse(override);
}
const externalPlugins = localStorage.getItem('experimental.external-plugins') === 'true';
if (externalPlugins && config.$references && config.$references.length > 0) {
const plugins = config.$references.map(
(name, idx) => this.loadConfig(`${this.pluginsPath}/${name}`, idx)
);
@ -129,6 +139,7 @@ export class ExtensionService implements RuleContext {
this.openWithActions = this.loadViewerOpenWith(config);
this.createActions = this.loadCreateActions(config);
this.navbar = this.loadNavBar(config);
this.sidebar = this.loadSidebar(config);
}
protected loadConfig(url: string, order: number): Promise<{ order: number, config: ExtensionConfig }> {
@ -166,7 +177,7 @@ export class ExtensionService implements RuleContext {
return [];
}
protected loadViewerActions(config: ExtensionConfig) {
protected loadViewerActions(config: ExtensionConfig): Array<ContentActionRef> {
if (config && config.features && config.features.viewer) {
return (config.features.viewer.actions || [])
.sort(this.sortByOrder)
@ -184,7 +195,7 @@ export class ExtensionService implements RuleContext {
return [];
}
protected loadNavBar(config: ExtensionConfig): any {
protected loadNavBar(config: ExtensionConfig): Array<NavBarGroupRef> {
if (config && config.features) {
return (config.features.navbar || [])
.filter(entry => !entry.disabled)
@ -206,7 +217,16 @@ export class ExtensionService implements RuleContext {
};
});
}
return {};
return [];
}
protected loadSidebar(config: ExtensionConfig): Array<SidebarTabRef> {
if (config && config.features) {
return (config.features.sidebar || [])
.filter(entry => !entry.disabled)
.sort(this.sortByOrder);
}
return [];
}
protected loadViewerOpenWith(config: ExtensionConfig): Array<ContentActionRef> {
@ -271,6 +291,10 @@ export class ExtensionService implements RuleContext {
return this.navbar;
}
getSidebarTabs(): Array<SidebarTabRef> {
return this.sidebar;
}
getComponentById(id: string): Type<{}> {
return this.components[id];
}

View File

@ -0,0 +1,38 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export class SidebarTabRef {
id: string;
title: string;
component: string;
icon?: string;
disabled?: boolean;
order?: number;
rules?: {
visible?: string;
[key: string]: string;
};
}

View File

@ -4,7 +4,8 @@
"$version": "1.0.0",
"$references": [
"plugin1.json",
"plugin2.json"
"plugin2.json",
"dev.tools.json"
],
"rules": [
@ -727,6 +728,27 @@
}
}
]
}
},
"sidebar": [
{
"id": "app.sidebar.properties",
"order": 100,
"title": "APP.INFO_DRAWER.TABS.PROPERTIES",
"component": "app.components.tabs.metadata"
},
{
"id": "app.sidebar.comments",
"order": 200,
"title": "APP.INFO_DRAWER.TABS.COMMENTS",
"component": "app.components.tabs.comments"
},
{
"id": "app.sidebar.versions",
"order": 300,
"disabled": true,
"title": "APP.INFO_DRAWER.TABS.VERSIONS",
"component": "app.components.tabs.versions"
}
]
}
}

View File

@ -0,0 +1,376 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/Alfresco/alfresco-content-app/blob/development/extension.schema.json",
"title": "ACA Extension Schema",
"description": "Provides a validation schema for ACA extensions",
"definitions": {
"ruleRef": {
"type": "object",
"required": ["id", "type"],
"properties": {
"id": {
"description": "Unique rule definition id",
"type": "string"
},
"type": {
"description": "Rule evaluator type",
"type": "string"
},
"parameters": {
"description": "Rule evaluator parameters",
"type": "array",
"items": { "$ref": "#/definitions/ruleParameter" },
"minItems": 1
}
}
},
"ruleParameter": {
"type": "object",
"required": ["type", "value"],
"properties": {
"type": {
"description": "Rule parameter type",
"type": "string"
},
"value": {
"description": "Rule parameter value",
"type": "string"
},
"parameters": {
"description": "Parameters",
"type": "array",
"items": { "$ref": "#/definitions/ruleParameter" },
"minItems": 1
}
}
},
"routeRef": {
"type": "object",
"required": ["id", "path", "component"],
"properties": {
"id": {
"description": "Unique route reference identifier.",
"type": "string"
},
"path": {
"description": "Route path to register.",
"type": "string"
},
"component": {
"description": "Unique identifier for the Component to use with the route.",
"type": "string"
},
"layout": {
"description": "Unique identifier for the custom layout component to use.",
"type": "string"
},
"auth": {
"description": "List of the authentication guards to use with the route.",
"type": "array",
"items": {
"type": "string"
},
"minLength": 1,
"uniqueItems": true
},
"data": {
"description": "Custom data to pass to the activated route so that your components can access it",
"type": "object"
}
}
},
"actionRef": {
"type": "object",
"required": ["id", "type"],
"properties": {
"id": {
"description": "Unique action identifier",
"type": "string"
},
"type": {
"description": "Action type",
"type": "string"
},
"payload": {
"description": "Action payload value (string or expression)",
"type": "string"
}
}
},
"contentActionRef": {
"type": "object",
"required": ["id"],
"properties": {
"id": {
"description": "Unique action identifier.",
"type": "string"
},
"type": {
"description": "Element type",
"type": "string",
"enum": ["default", "button", "separator", "menu", "custom"]
},
"title": {
"description": "Element title",
"type": "string"
},
"description": {
"description": "Element description, used for the tooltips.",
"type": "string"
},
"description-disabled": {
"description": "Description to use when element is in the disabled state.",
"type": "string"
},
"order": {
"description": "Element order",
"type": "number"
},
"icon": {
"description": "Element icon",
"type": "string"
},
"disabled": {
"description": "Toggles disabled state",
"type": "boolean"
},
"component": {
"description": "Custom component id (requires type to be 'custom')",
"type": "string"
},
"children": {
"description": "Child entries for the container types.",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
},
"actions": {
"description": "Element actions",
"type": "object",
"properties": {
"click": {
"description": "Action reference for the click handler",
"type": "string"
}
}
},
"rules": {
"description": "Element rules",
"type": "object",
"properties": {
"enabled": {
"description": "Rule to evaluate the enabled state",
"type": "string"
},
"visible": {
"description": "Rule to evaluate the visibility state",
"type": "string"
}
}
}
}
},
"navBarLinkRef": {
"type": "object",
"required": ["id", "icon", "title", "route"],
"properties": {
"id": {
"description": "Unique identifier",
"type": "string"
},
"icon": {
"description": "Element icon",
"type": "string"
},
"title": {
"description": "Element title",
"type": "string"
},
"route": {
"description": "Route reference identifier",
"type": "string"
},
"description": {
"description": "Element description or tooltip",
"type": "string"
},
"order": {
"description": "Element order",
"type": "number"
},
"disabled": {
"description": "Toggles the disabled state",
"type": "boolean"
}
}
},
"navBarGroupRef": {
"type": "object",
"required": ["id", "items"],
"properties": {
"id": {
"description": "Unique identifier for the navigation group",
"type": "string"
},
"items": {
"description": "Navigation group items",
"type": "array",
"items": { "$ref": "#/definitions/navBarLinkRef" },
"minItems": 1
},
"order": {
"description": "Group order",
"type": "number"
},
"disabled": {
"description": "Toggles the disabled state",
"type": "boolean"
}
}
},
"sidebarTabRef": {
"type": "object",
"required": ["id", "component"],
"properties": {
"id": {
"description": "Unique identifier for the navigation group",
"type": "string"
},
"title": {
"description": "Element title",
"type": "string"
},
"component": {
"description": "Component id",
"type": "string"
},
"icon": {
"description": "Material icon name",
"type": "string"
},
"disabled": {
"description": "Toggles disabled state",
"type": "boolean"
},
"order": {
"description": "Element order",
"type": "number"
},
"rules": {
"description": "Element rules",
"type": "object",
"properties": {
"visible": {
"description": "Rule to evaluate the visibility state",
"type": "string"
}
}
}
}
}
},
"type": "object",
"required": ["$name", "$version"],
"properties": {
"$name": {
"description": "Extension name",
"type": "string"
},
"$version": {
"description": "Extension version",
"type": "string"
},
"$description": {
"description": "Brief description on what the extension does"
},
"$references": {
"description": "References to external files",
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
},
"rules": {
"description": "List of rule definitions",
"type": "array",
"items": { "$ref": "#/definitions/ruleRef" },
"minItems": 1
},
"routes": {
"description": "List of custom application routes",
"type": "array",
"items": { "$ref": "#/definitions/routeRef" },
"minItems": 1
},
"actions": {
"description": "List of action definitions",
"type": "array",
"items": { "$ref": "#/definitions/actionRef" },
"minItems": 1
},
"features": {
"description": "Application-specific features and extensions",
"type": "object",
"properties": {
"create": {
"description": "The [New] menu component extensions",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
},
"viewer": {
"description": "Viewer component extensions",
"type": "object",
"properties": {
"openWith": {
"description": "The [Open With] menu extensions",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
},
"actions": {
"description": "Content actions (toolbar, context menus, etc.)",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
}
}
},
"navbar": {
"description": "Navigation bar extensions",
"type": "array",
"items": { "$ref": "#/definitions/navBarGroupRef" },
"minItems": 1
},
"sidebar": {
"description": "Sidebar extensions",
"type": "array",
"items": { "$ref": "#/definitions/sidebarTabRef" },
"minItems": 1
},
"content": {
"description": "Main application content extensions",
"type": "object",
"properties": {
"actions": {
"description": "Content actions (toolbar, context menus, etc.)",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
},
"contextActions": {
"description": "Content actions (toolbar, context menus, etc.)",
"type": "array",
"items": { "$ref": "#/definitions/contentActionRef" },
"minItems": 1
}
}
}
}
}
}
}

View File

@ -0,0 +1,36 @@
{
"$schema": "../../../extension.schema.json",
"$version": "1.0.0",
"$name": "dev.tools",
"$description": "ACA dev tools",
"routes": [
{
"id": "app.dev.tools",
"component": "app.dev.tools.component",
"path": "dev-tools",
"data": {
"configPath": "assets/app.extensions.json",
"pluginsPath": "assets/plugins",
"schemaUri": "../../extension.schema.json",
"schemaPath": "assets/extension.schema.json"
}
}
],
"features": {
"navbar": [
{
"id": "app.navbar.dev.tools",
"items": [
{
"id": "app.navbar.dev.tools.main",
"icon": "build",
"title": "dev tools",
"route": "app.dev.tools"
}
]
}
]
}
}

View File

@ -95,3 +95,38 @@ import 'zone.js/dist/zone'; // Included with Angular CLI.
* Need to import at least one locale-data with intl.
*/
// import 'intl/locale-data/jsonp/en';
// workaround for https://github.com/Microsoft/monaco-editor/issues/790
Promise.all = function(values: any): Promise<any> {
let resolve: (v: any) => void;
let reject: (v: any) => void;
const promise = new this((res, rej) => {
resolve = res;
reject = rej;
});
let count = 0;
let index = 0;
const resolvedValues: any[] = [];
for (let value of values) {
if (!(value && value.then)) {
value = this.resolve(value);
}
value.then(
(idx => (val: any) => {
resolvedValues[idx] = val;
count--;
if (!count) {
resolve(resolvedValues);
}
})(index),
reject
);
count++;
index++;
}
if (!count) {
resolve(resolvedValues);
}
return promise;
};

View File

@ -2,9 +2,7 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
"types": []
},
"exclude": ["node_modules", "test.ts", "**/*.spec.ts"]
"exclude": ["test.ts", "**/*.spec.ts"]
}

View File

@ -2,9 +2,6 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"baseUrl": "./",
"module": "commonjs",
"target": "es5",
"types": ["jasmine", "node"]
},
"files": ["test.ts", "polyfills.ts"],

View File

@ -17,7 +17,15 @@
"dom"
],
"module": "es2015",
"baseUrl": "./"
"baseUrl": "./",
"paths": {
"aca-dev-tools": [
"dist/aca-dev-tools"
],
"aca-dev-tools/*": [
"dist/aca-dev-tools/*"
]
}
},
"exclude": [
"node_modules"