From 1ba4dc6954da243a087091108e6037f0f7c27ea6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 9 Sep 2019 17:16:33 +0200 Subject: [PATCH] Introduce registerAndGetAmdImageURL and inline such images in the bundle phase in .js --- build/gulpfile.vscode.js | 1 + build/lib/optimize.js | 40 ++++++++++++++++ build/lib/optimize.ts | 47 +++++++++++++++++++ src/vs/base/common/amd.ts | 8 ++++ .../codeEditor/browser/toggleWordWrap.ts | 11 +++-- .../files/browser/fileActions.contribution.ts | 3 +- 6 files changed, 104 insertions(+), 6 deletions(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3e97ef2fe3b..baa2b749486 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -93,6 +93,7 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series( resources: vscodeResources, loaderConfig: common.loaderConfig(nodeModules), out: 'out-vscode', + inlineAmdImages: true, bundleInfo: undefined }) )); diff --git a/build/lib/optimize.js b/build/lib/optimize.js index ae2e42ec65e..d7d58bf09ab 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -5,6 +5,7 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); +const fs = require("fs"); const gulp = require("gulp"); const concat = require("gulp-concat"); const minifyCSS = require("gulp-cssnano"); @@ -132,6 +133,14 @@ function optimizeTask(opts) { if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); } + if (opts.inlineAmdImages) { + try { + result = inlineAmdImages(src, result); + } + catch (err) { + return bundlesStream.emit('error', JSON.stringify(err)); + } + } toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); // Remove css inlined resources const filteredResources = resources.slice(); @@ -167,6 +176,37 @@ function optimizeTask(opts) { }; } exports.optimizeTask = optimizeTask; +function inlineAmdImages(src, result) { + for (const outputFile of result.files) { + for (const sourceFile of outputFile.sources) { + if (sourceFile.path && /\.js$/.test(sourceFile.path)) { + sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => { + let imagePath = m0; + // remove `` or '' + if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`') + || (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) { + imagePath = imagePath.substr(1, imagePath.length - 2); + } + if (!/\.(png|svg)$/.test(imagePath)) { + console.log(`original: ${_}`); + return _; + } + const repoLocation = path.join(src, imagePath); + const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation); + if (!fs.existsSync(absoluteLocation)) { + throw new Error(`Invalid amd image url in file ${sourceFile.path}: ${imagePath}`); + } + const fileContents = fs.readFileSync(absoluteLocation); + const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png'; + // Mark the file as inlined so we don't ship it by itself + result.cssInlinedResources.push(repoLocation); + return `("data:${mime};base64,${fileContents.toString('base64')}")`; + }); + } + } + } + return result; +} /** * Wrap around uglify and allow the preserveComments function * to have a file "context" to include our copyright only once per file. diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 9380d5880a3..bd5cffb54b7 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -6,6 +6,7 @@ 'use strict'; import * as es from 'event-stream'; +import * as fs from 'fs'; import * as gulp from 'gulp'; import * as concat from 'gulp-concat'; import * as minifyCSS from 'gulp-cssnano'; @@ -159,6 +160,10 @@ export interface IOptimizeTaskOpts { * (emit bundleInfo.json file) */ bundleInfo: boolean; + /** + * replace calls to `registerAndGetAmdImageURL` with data uris + */ + inlineAmdImages: boolean; /** * (out folder name) */ @@ -192,6 +197,14 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr bundle.bundle(entryPoints, loaderConfig, function (err, result) { if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); } + if (opts.inlineAmdImages) { + try { + result = inlineAmdImages(src, result); + } catch (err) { + return bundlesStream.emit('error', JSON.stringify(err)); + } + } + toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); // Remove css inlined resources @@ -236,6 +249,40 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr }; } +function inlineAmdImages(src: string, result: bundle.IBundleResult): bundle.IBundleResult { + for (const outputFile of result.files) { + for (const sourceFile of outputFile.sources) { + if (sourceFile.path && /\.js$/.test(sourceFile.path)) { + sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => { + let imagePath = m0; + // remove `` or '' + if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`') + || (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) { + imagePath = imagePath.substr(1, imagePath.length - 2); + } + if (!/\.(png|svg)$/.test(imagePath)) { + console.log(`original: ${_}`); + return _; + } + const repoLocation = path.join(src, imagePath); + const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation); + if (!fs.existsSync(absoluteLocation)) { + throw new Error(`Invalid amd image url in file ${sourceFile.path}: ${imagePath}`); + } + const fileContents = fs.readFileSync(absoluteLocation); + const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png'; + + // Mark the file as inlined so we don't ship it by itself + result.cssInlinedResources.push(repoLocation); + + return `("data:${mime};base64,${fileContents.toString('base64')}")`; + }); + } + } + } + return result; +} + declare class FileWithCopyright extends VinylFile { public __hasOurCopyright: boolean; } diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index d985c46deeb..90497f4b418 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -8,3 +8,11 @@ import { URI } from 'vs/base/common/uri'; export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { return URI.parse(requirefn.toUrl(relativePath)).fsPath; } + +/** + * Reference a resource that might be inlined. + * Do not rename this method unless you adopt the build scripts. + */ +export function registerAndGetAmdImageURL(absolutePath: string): string { + return require.toUrl(absolutePath); +} diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 9032288864d..83b3a7bb271 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -19,6 +19,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { DefaultSettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; +import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; @@ -183,7 +184,7 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution let currentlyApplyingEditorConfig = false; this._register(editor.onDidChangeConfiguration((e) => { - if (!e.hasChanged(EditorOption.wrappingInfo) || !e.hasChanged(EditorOption.inDiffEditor)) { + if (!e.hasChanged(EditorOption.wrappingInfo) && !e.hasChanged(EditorOption.inDiffEditor)) { return; } const options = this.editor.getOptions(); @@ -276,8 +277,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { id: TOGGLE_WORD_WRAP_ID, title: nls.localize('unwrapMinified', "Disable wrapping for this file"), iconLocation: { - dark: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')), - light: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg')) + dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')), + light: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg')) } }, group: 'navigation', @@ -293,8 +294,8 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { id: TOGGLE_WORD_WRAP_ID, title: nls.localize('wrapMinified', "Enable wrapping for this file"), iconLocation: { - dark: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')), - light: URI.parse(require.toUrl('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg')) + dark: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-dark.svg')), + light: URI.parse(registerAndGetAmdImageURL('vs/workbench/contrib/codeEditor/browser/word-wrap-light.svg')) } }, group: 'navigation', diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 40a0ec28bf2..97119b32016 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -26,6 +26,7 @@ import { Schemas } from 'vs/base/common/network'; import { SupportsWorkspacesContext, IsWebContext, RemoteFileDialogContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenLocalFileFolderCommand, OpenFileAction, OpenFolderAction, OpenLocalFileCommand, OpenLocalFolderCommand, OpenWorkspaceAction, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions'; +import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; // Contribute Global Actions const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; @@ -223,7 +224,7 @@ function appendEditorTitleContextMenuItem(id: string, title: string, when: Conte // Editor Title Menu for Conflict Resolution appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { - light: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/check-light.svg`)), + light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-light.svg`)), dark: URI.parse(require.toUrl(`vs/workbench/contrib/files/browser/media/check-dark.svg`)) }, -10, acceptLocalChangesCommand); appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { -- GitLab