Feat/generator 2 iteration (#398)

* chore: eslintrc fix

* chore: update package json with new build scripts, correct build folder

* feat: build ui scripts

* chore: start fixing eslint

* chore: fix interfaces name

* feat: add copy dist source to build with cp (for now)
This commit is contained in:
Alessio Occhipinti 2019-12-23 17:34:01 +01:00 committed by Mattia Astorino
parent bf9a2bb6ed
commit c040e556ae
15 changed files with 225 additions and 168 deletions

View file

@ -4,6 +4,11 @@
"xo-typescript"
],
"rules": {
"@typescript-eslint/indent": ["error", 2, { "SwitchCase": 1 }]
"@typescript-eslint/indent": ["error", 2, { "SwitchCase": 1 }],
"@typescript-eslint/member-naming": 0,
"@typescript-eslint/interface-name-prefix": ["error", { "prefixWithI": "always" }]
},
"env": {
"browser": true
}
}

2
env.ts
View file

@ -1,3 +1,5 @@
import * as path from 'path';
export const SRC_FOLDER_PATH = path.resolve('./src');
export const BUILD_FOLDER_PATH = path.resolve('./build');
export const TS_BUILD_FOLDER_PATH = path.resolve('./dist');

View file

@ -24,16 +24,12 @@
"vscode": ">=1.39.0"
},
"scripts": {
"bu": "yarn cleanup && yarn build:ts && yarn build:generate-themes",
"lint": "eslint .",
"build": "yarn cleanup && yarn build:ts && yarn build-themes && yarn build-ui",
"build": "yarn cleanup && yarn build:ts && yarn build:generate-themes && yarn build:ui",
"cleanup": "rimraf build && rimraf dist",
"lint": "eslint .",
"build:ui": "node dist/scripts/ui/index.js",
"build:generate-themes": "node dist/scripts/generator/index.js",
"build-ui": "gulp build:copy-ui && yarn build-ui-release-notes",
"build-ui-release-notes": "browserify out/src/webviews/ui/release-notes/index.js > out/ui/release-notes.js",
"build-ui-only": "yarn cleanup && yarn build-ts && yarn build-ui",
"build:ts": "tsc -p ./tsconfig.json",
"test": "tslint **.ts",
"build:ts": "tsc -p ./tsconfig.json && cp -r dist/src/ build",
"postinstall": "node ./node_modules/vscode/bin/install && opencollective postinstall && tsc -p tsconfig.json"
},
"categories": [
@ -48,7 +44,7 @@
"*"
],
"extensionKind": "ui",
"main": "./out/src/material.theme.config",
"main": "./build/material.theme.config",
"contributes": {
"commands": [
{
@ -95,52 +91,52 @@
"themes": [
{
"label": "Material Theme",
"path": "./out/themes/Material-Theme-Default.json",
"path": "./build/themes/Material-Theme-Default.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme High Contrast",
"path": "./out/themes/Material-Theme-Default-High-Contrast.json",
"path": "./build/themes/Material-Theme-Default-High-Contrast.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Darker",
"path": "./out/themes/Material-Theme-Darker.json",
"path": "./build/themes/Material-Theme-Darker.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Darker High Contrast",
"path": "./out/themes/Material-Theme-Darker-High-Contrast.json",
"path": "./build/themes/Material-Theme-Darker-High-Contrast.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Palenight",
"path": "./out/themes/Material-Theme-Palenight.json",
"path": "./build/themes/Material-Theme-Palenight.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Palenight High Contrast",
"path": "./out/themes/Material-Theme-Palenight-High-Contrast.json",
"path": "./build/themes/Material-Theme-Palenight-High-Contrast.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Ocean",
"path": "./out/themes/Material-Theme-Ocean.json",
"path": "./build/themes/Material-Theme-Ocean.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Ocean High Contrast",
"path": "./out/themes/Material-Theme-Ocean-High-Contrast.json",
"path": "./build/themes/Material-Theme-Ocean-High-Contrast.json",
"uiTheme": "vs-dark"
},
{
"label": "Material Theme Lighter",
"path": "./out/themes/Material-Theme-Lighter.json",
"path": "./build/themes/Material-Theme-Lighter.json",
"uiTheme": "vs"
},
{
"label": "Material Theme Lighter High Contrast",
"path": "./out/themes/Material-Theme-Lighter-High-Contrast.json",
"path": "./build/themes/Material-Theme-Lighter-High-Contrast.json",
"uiTheme": "vs"
}
]
@ -163,6 +159,7 @@
"devDependencies": {
"@babel/register": "7.4.4",
"@moxer/vscode-theme-generator": "1.1.0",
"@types/browserify": "12.0.36",
"@types/gulp-if": "0.0.33",
"@types/gulp-util": "3.0.34",
"@types/mustache": "0.8.32",

View file

@ -25,9 +25,13 @@ const generate = async (): Promise<void> => {
});
};
try {
generate();
} catch (error) {
console.error(error);
process.exit(1);
}
const run = async (): Promise<void> => {
try {
await generate();
} catch (error) {
console.error('ERROR build:generate-themes', error);
process.exit(1);
}
};
run();

44
scripts/ui/index.ts Normal file
View file

@ -0,0 +1,44 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import browserify from 'browserify';
import {BUILD_FOLDER_PATH, SRC_FOLDER_PATH, TS_BUILD_FOLDER_PATH} from '../../env';
const UI_FOLDER_PATH = path.join(SRC_FOLDER_PATH, 'webviews', 'ui');
const UI_JS_FOLDER_PATH = path.join(TS_BUILD_FOLDER_PATH, 'src', 'webviews', 'ui');
const UI_FOLDER_BUILD_PATH = path.join(BUILD_FOLDER_PATH, 'ui');
const copyStatics = async (): Promise<void[]> => {
const paths = [{
src: path.join(UI_FOLDER_PATH, 'release-notes', 'release-notes.html'),
dest: path.join(UI_FOLDER_BUILD_PATH, 'release-notes.html')
}, {
src: path.join(UI_FOLDER_PATH, 'release-notes', 'style.css'),
dest: path.join(UI_FOLDER_BUILD_PATH, 'style.css')
}];
return Promise.all(paths.map(async path => fs.copyFile(path.src, path.dest)));
};
const buildJs = async (type: 'release-notes'): Promise<void> => {
const jsBuildPath = path.join(UI_FOLDER_BUILD_PATH, `${type}.js`);
const b = browserify();
await fs.createFile(jsBuildPath);
const jsBuildFileStream = fs.createWriteStream(jsBuildPath);
b.add(path.join(UI_JS_FOLDER_PATH, type, 'index.js'));
b.bundle().pipe(jsBuildFileStream);
return Promise.resolve();
};
const run = async (): Promise<void> => {
try {
await fs.mkdirp(UI_FOLDER_BUILD_PATH);
await copyStatics();
await buildJs('release-notes');
} catch (error) {
console.error('ERROR build:ui:', error);
process.exit(1);
}
};
run();

View file

@ -10,7 +10,7 @@ import checkInstallation from './helpers/check-installation';
import writeChangelog from './helpers/write-changelog';
import {ReleaseNotesWebview} from './webviews/ReleaseNotes';
export async function activate(context: ExtensionContext) {
export async function activate(context: ExtensionContext): Promise<void> {
const installationType = checkInstallation();
const releaseNotesView = new ReleaseNotesWebview(context);
@ -32,5 +32,5 @@ export async function activate(context: ExtensionContext) {
await updateAccent(accentPicked);
});
Commands.registerCommand('materialTheme.showReleaseNotes', () => releaseNotesView.show());
Commands.registerCommand('materialTheme.showReleaseNotes', async () => releaseNotesView.show());
}

View file

@ -1,13 +1,6 @@
import {WebviewController} from './Webview';
import {
ExtensionContext
} from 'vscode';
export class ReleaseNotesWebview extends WebviewController<{}> {
constructor(context: ExtensionContext) {
super(context);
}
get filename(): string {
return 'release-notes.html';
}
@ -24,7 +17,7 @@ export class ReleaseNotesWebview extends WebviewController<{}> {
* This will be called by the WebviewController when init the view
* passing as `window.bootstrap` to the view.
*/
getBootstrap() {
getBootstrap(): {} {
return {};
}
}

View file

@ -1,18 +1,12 @@
import {WebviewController} from './Webview';
import {
workspace as Workspace,
ExtensionContext
workspace as Workspace
} from 'vscode';
import {SettingsBootstrap} from './interfaces';
import {ISettingsBootstrap} from './interfaces';
import {getCustomSettings} from '../helpers/settings';
import {getDefaultValues} from '../helpers/fs';
export class SettingsWebview extends WebviewController<SettingsBootstrap> {
constructor(context: ExtensionContext) {
super(context);
}
export class SettingsWebview extends WebviewController<ISettingsBootstrap> {
get filename(): string {
return 'settings.html';
}
@ -25,26 +19,26 @@ export class SettingsWebview extends WebviewController<SettingsBootstrap> {
return 'Material Theme Settings';
}
private getAvailableScopes(): ['user' | 'workspace', string][] {
const scopes: ['user' | 'workspace', string][] = [['user', 'User']];
return scopes
.concat(
Workspace.workspaceFolders !== undefined && Workspace.workspaceFolders.length ?
['workspace', 'Workspace'] :
[]
);
}
/**
* This will be called by the WebviewController when init the view
* passing as `window.bootstrap` to the view.
*/
getBootstrap() {
getBootstrap(): ISettingsBootstrap {
return {
config: getCustomSettings(),
defaults: getDefaultValues(),
scope: 'user',
scopes: this.getAvailableScopes()
} as SettingsBootstrap;
};
}
private getAvailableScopes(): Array<['user' | 'workspace', string]> {
const scopes: Array<['user' | 'workspace', string]> = [['user', 'User']];
return scopes
.concat(
Workspace.workspaceFolders?.length ?
['workspace', 'Workspace'] :
[]
);
}
}

View file

@ -13,13 +13,13 @@ import {
} from 'vscode';
import {getCustomSettings} from '../helpers/settings';
import {Invalidates, Message, SettingsChangedMessage} from './interfaces';
import {Invalidates, Message, ISettingsChangedMessage} from './interfaces';
export abstract class WebviewController<TBootstrap> extends Disposable {
private panel: WebviewPanel | undefined;
private disposablePanel: Disposable | undefined;
private invalidateOnVisible: Invalidates;
private context: ExtensionContext;
private readonly context: ExtensionContext;
constructor(context: ExtensionContext) {
// Applying dispose callback for our disposable function
@ -28,92 +28,12 @@ export abstract class WebviewController<TBootstrap> extends Disposable {
this.context = context;
}
abstract get filename(): string;
abstract get id(): string;
abstract get title(): string;
abstract getBootstrap(): TBootstrap;
dispose() {
dispose(): void {
if (this.disposablePanel) {
this.disposablePanel.dispose();
}
}
private async getHtml(): Promise<string> {
const doc = await Workspace
.openTextDocument(this.context.asAbsolutePath(path.join('out/ui', this.filename)));
return doc.getText();
}
private postMessage(message: Message, invalidates: Invalidates = 'all') {
if (this.panel === undefined) {
return false;
}
const result = this.panel.webview.postMessage(message);
// If post was ok, update invalidateOnVisible if different than default
if (!result && this.invalidateOnVisible !== 'all') {
this.invalidateOnVisible = invalidates;
}
return result;
}
private postUpdatedConfiguration() {
// Post full raw configuration
return this.postMessage({
type: 'settingsChanged',
config: getCustomSettings()
} as SettingsChangedMessage, 'config');
}
private onPanelDisposed() {
if (this.disposablePanel) {
this.disposablePanel.dispose();
}
this.panel = undefined;
}
private onViewStateChanged(event: WebviewPanelOnDidChangeViewStateEvent) {
console.log('WebviewEditor.onViewStateChanged', event.webviewPanel.visible);
if (!this.invalidateOnVisible || !event.webviewPanel.visible) {
return;
}
// Update the view since it can be outdated
const invalidContext = this.invalidateOnVisible;
this.invalidateOnVisible = undefined;
switch (invalidContext) {
case 'config':
// Post the new configuration to the view
return this.postUpdatedConfiguration();
default:
return this.show();
}
}
protected async onMessageReceived(event: Message) {
if (event === null) {
return;
}
console.log(`WebviewEditor.onMessageReceived: type=${event.type}, data=${JSON.stringify(event)}`);
switch (event.type) {
case 'saveSettings':
// TODO: update settings
return;
default:
return;
}
}
async show(): Promise<void> {
const html = await this.getHtml();
@ -154,4 +74,82 @@ export abstract class WebviewController<TBootstrap> extends Disposable {
this.panel.webview.html = fullHtml;
}
protected onMessageReceived(event: Message): void {
if (event === null) {
return;
}
console.log(`WebviewEditor.onMessageReceived: type=${event.type}, data=${JSON.stringify(event)}`);
switch (event.type) {
case 'saveSettings':
// TODO: update settings
break;
default:
break;
}
}
private async getHtml(): Promise<string> {
const doc = await Workspace
.openTextDocument(this.context.asAbsolutePath(path.join('out/ui', this.filename)));
return doc.getText();
}
private async postMessage(message: Message, invalidates: Invalidates = 'all'): Promise<boolean> {
if (this.panel === undefined) {
return false;
}
const result = await this.panel.webview.postMessage(message);
// If post was ok, update invalidateOnVisible if different than default
if (!result && this.invalidateOnVisible !== 'all') {
this.invalidateOnVisible = invalidates;
}
return result;
}
private async postUpdatedConfiguration(): Promise<boolean> {
// Post full raw configuration
return this.postMessage({
type: 'settingsChanged',
config: getCustomSettings()
} as ISettingsChangedMessage, 'config');
}
private onPanelDisposed(): void {
if (this.disposablePanel) {
this.disposablePanel.dispose();
}
this.panel = undefined;
}
private async onViewStateChanged(event: WebviewPanelOnDidChangeViewStateEvent): Promise<boolean | void> {
console.log('WebviewEditor.onViewStateChanged', event.webviewPanel.visible);
if (!this.invalidateOnVisible || !event.webviewPanel.visible) {
return;
}
// Update the view since it can be outdated
const invalidContext = this.invalidateOnVisible;
this.invalidateOnVisible = undefined;
switch (invalidContext) {
case 'config':
// Post the new configuration to the view
return this.postUpdatedConfiguration();
default:
return this.show();
}
}
abstract get filename(): string;
abstract get id(): string;
abstract get title(): string;
abstract getBootstrap(): TBootstrap;
}

View file

@ -2,52 +2,55 @@ import {IThemeCustomSettings} from '../interfaces/itheme-custom-properties';
import {IDefaults} from '../interfaces/idefaults';
export interface IChangeType {
children: {
text: String;
}[];
children: Array<{
text: string;
}>;
}
export interface IPost {
title: String;
version: String;
title: string;
version: string;
fixed: IChangeType[];
new: IChangeType[];
breaking: IChangeType[];
}
export interface IPostNormalized {
title: String;
version: String;
fixed: String[];
new: String[];
breaking: String[];
title: string;
version: string;
fixed: string[];
new: string[];
breaking: string[];
}
export interface SettingsChangedMessage {
export interface ISettingsChangedMessage {
type: 'settingsChanged';
config: IThemeCustomSettings;
}
export interface SaveSettingsMessage {
export interface ISaveSettingsMessage {
type: 'saveSettings';
changes: {
[key: string]: any;
[key: string]: any;
};
removes: string[];
scope: 'user' | 'workspace';
uri: string;
}
export type Message = SaveSettingsMessage | SettingsChangedMessage;
export type Message = ISaveSettingsMessage | ISettingsChangedMessage;
export type Invalidates = 'all' | 'config' | undefined;
export interface Bootstrap {
export interface IBootstrap {
config: IThemeCustomSettings;
}
export interface SettingsBootstrap extends Bootstrap {
export interface ISettingsBootstrap extends IBootstrap {
scope: 'user' | 'workspace';
scopes: ['user' | 'workspace', string][];
scopes: Array<['user' | 'workspace', string]>;
defaults: IDefaults;
}
declare global {
interface Window { bootstrap: Bootstrap | SettingsBootstrap | {}; }
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
interface Window {
bootstrap: IBootstrap | ISettingsBootstrap | {};
}
}

View file

@ -2,18 +2,19 @@ import * as sanityClient from '@sanity/client';
import {IPost, IPostNormalized} from '../../interfaces';
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const getClient = () => sanityClient({
projectId: 'v475t82f',
dataset: 'production'
});
const getReleaseNotes = (): Promise<object[]> => {
const getReleaseNotes = async (): Promise<object[]> => {
const query = '*[_type == "release"] | order(version desc)';
const client = getClient();
return client.fetch(query);
};
const renderTemplate = (posts: IPostNormalized[]) => {
const renderTemplate = (posts: IPostNormalized[]): string => {
return `${posts.reduce((acc, {version, title, fixed, new: newItems, breaking}) => acc.concat(`<section class="Release">
<header class="Release__Header">
<span class="Release__Number">${version}</span>
@ -21,11 +22,11 @@ const renderTemplate = (posts: IPostNormalized[]) => {
</header>
<ul class="Release-List">
${fixed.reduce((accc: string, src) =>
src.length > 0 ? accc.concat(`<li data-type="fixed">${src}</li>`) : '', '')}
src.length > 0 ? accc.concat(`<li data-type="fixed">${src}</li>`) : '', '')}
${newItems.reduce((accc: string, src) =>
src.length > 0 ? accc.concat(`<li data-type="new">${src}</li>`) : '', '')}
src.length > 0 ? accc.concat(`<li data-type="new">${src}</li>`) : '', '')}
${breaking.reduce((accc: string, src) =>
src.length > 0 ? accc.concat(`<li data-type="breaking">${src}</li>`) : '', '')}
src.length > 0 ? accc.concat(`<li data-type="breaking">${src}</li>`) : '', '')}
</ul>
</section>`), '')}`;
};

View file

@ -1,17 +1,17 @@
import {SettingsBootstrap} from '../../interfaces';
import {ISettingsBootstrap} from '../../interfaces';
import accentsSelector from './lib/accents-selector';
const run = () => {
const run = (): void => {
bind();
const {config, defaults} = window.bootstrap as SettingsBootstrap;
const {config, defaults} = window.bootstrap as ISettingsBootstrap;
accentsSelector('[data-setting="accentSelector"]', defaults.accents, config.accent);
console.log(defaults);
console.log(config);
};
const bind = () => {
const bind = (): void => {
document.querySelector('#fixIconsCTA').addEventListener('click', () => {
console.log('Test click');
});

View file

@ -9,7 +9,7 @@ const templateSingleAccent = (accentName: string, accentColor: string): string =
`;
};
export default (containerSelector: string, accentsObject: IAccents, currentAccent: string) => {
export default (containerSelector: string, accentsObject: IAccents, currentAccent: string): void => {
const container = document.querySelector(containerSelector);
for (const accentKey of Object.keys(accentsObject)) {

View file

@ -7,6 +7,7 @@
"es7",
"dom"
],
"esModuleInterop": true,
"sourceMap": true,
"allowUnreachableCode": false,
"noUnusedLocals": true,

View file

@ -112,6 +112,14 @@
resolved "https://registry.yarnpkg.com/@sanity/timed-out/-/timed-out-4.0.2.tgz#c9f61f9a1609baa1eb3e4235a24ea2a775022cdf"
integrity sha512-NBDKGj14g9Z+bopIvZcQKWCzJq5JSrdmzRR1CS+iyA3Gm8SnIWBfZa7I3mTg2X6Nu8LQXG0EPKXdOGozLS4i3w==
"@types/browserify@12.0.36":
version "12.0.36"
resolved "https://registry.yarnpkg.com/@types/browserify/-/browserify-12.0.36.tgz#a7b662550bd4102b38ba83ef4ad6db871ea91331"
integrity sha512-hYXvPod5upkYTC7auziOATFsu/0MGxozbzNI80sZV044JTF7UtstHeNOM52b+bg7/taZ3fheK7oeb+jpm4C0/w==
dependencies:
"@types/insert-module-globals" "*"
"@types/node" "*"
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@ -161,6 +169,13 @@
"@types/vinyl" "*"
chalk "^2.2.0"
"@types/insert-module-globals@*":
version "7.0.1"
resolved "https://registry.yarnpkg.com/@types/insert-module-globals/-/insert-module-globals-7.0.1.tgz#234f9263f6b315088287e3597d7e98033804a031"
integrity sha512-qtSfo/jdYHO4jNO6QCp4CwR/TPrvR39Yan5K4nPU1iCmxcnTWiERKDXcvFGuXEmfpjrHeOCvrZPa0UrUsy+mvA==
dependencies:
"@types/node" "*"
"@types/json-schema@^7.0.3":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"