From 3e4111b06133bb274ccf7168eaa31f090f5cc5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Str=C3=B6mberg?= Date: Tue, 31 Jul 2018 22:54:49 +0200 Subject: [PATCH] Swich to a combo of TS and CLJS toolchains --- .vscode/tasks.json | 30 +- .../calva/connector.js => calva/connector.ts | 11 +- .../calva/extension.js => calva/extension.ts | 17 +- .../providers/annotations.ts | 4 +- .../providers/completion.ts | 45 +- .../content.js => calva/providers/content.ts | 13 +- .../providers/definition.ts | 19 +- .../hover.js => calva/providers/hover.ts | 10 +- .../repl/client.js => calva/repl/client.ts | 4 +- .../repl/middleware/evaluate.ts | 13 +- .../lint.js => calva/repl/middleware/lint.ts | 6 +- .../repl/middleware/select.ts | 6 +- .../repl/middleware/testRunner.ts | 14 +- src/main/calva/shadow.js => calva/shadow.ts | 4 +- src/main/calva/state.js => calva/state.ts | 11 +- src/main/calva/status.js => calva/status.ts | 0 .../calva/statusbar.js => calva/statusbar.ts | 2 +- .../calva/terminal.js => calva/terminal.ts | 2 +- .../calva/utilities.js => calva/utilities.ts | 6 +- {src/main => lib-src}/calva/greet.cljs | 14 +- lib-src/calva/migration.cljs | 14 + {src/main => lib-src}/calva/repl/message.cljs | 51 +- {src/main => lib-src}/calva/repl/nrepl.cljs | 6 + lib/calva.js | 3059 ++++++ package-lock.json | 8592 ++++++----------- package.json | 25 +- shadow-cljs.edn | 17 +- src/main/calva/extension.cljs | 10 - src/main/calva/migration.cljs | 4 - src/main/calva/repl/client.cljs | 20 - test-out/tests.js | 3038 ++++++ tsconfig.json | 16 + 32 files changed, 9449 insertions(+), 5634 deletions(-) rename src/main/calva/connector.js => calva/connector.ts (97%) rename src/main/calva/extension.js => calva/extension.ts (94%) rename src/main/calva/providers/annotations.js => calva/providers/annotations.ts (94%) rename src/main/calva/providers/completion.js => calva/providers/completion.ts (68%) rename src/main/calva/providers/content.js => calva/providers/content.ts (79%) rename src/main/calva/providers/definition.js => calva/providers/definition.ts (80%) rename src/main/calva/providers/hover.js => calva/providers/hover.ts (93%) rename src/main/calva/repl/client.js => calva/repl/client.ts (87%) rename src/main/calva/repl/middleware/evaluate.js => calva/repl/middleware/evaluate.ts (95%) rename src/main/calva/repl/middleware/lint.js => calva/repl/middleware/lint.ts (95%) rename src/main/calva/repl/middleware/select.js => calva/repl/middleware/select.ts (92%) rename src/main/calva/repl/middleware/testRunner.js => calva/repl/middleware/testRunner.ts (95%) rename src/main/calva/shadow.js => calva/shadow.ts (92%) rename src/main/calva/state.js => calva/state.ts (80%) rename src/main/calva/status.js => calva/status.ts (100%) rename src/main/calva/statusbar.js => calva/statusbar.ts (98%) rename src/main/calva/terminal.js => calva/terminal.ts (99%) rename src/main/calva/utilities.js => calva/utilities.ts (98%) rename {src/main => lib-src}/calva/greet.cljs (81%) create mode 100644 lib-src/calva/migration.cljs rename {src/main => lib-src}/calva/repl/message.cljs (75%) rename {src/main => lib-src}/calva/repl/nrepl.cljs (98%) create mode 100644 lib/calva.js delete mode 100644 src/main/calva/extension.cljs delete mode 100644 src/main/calva/migration.cljs delete mode 100644 src/main/calva/repl/client.cljs create mode 100644 test-out/tests.js create mode 100644 tsconfig.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0ee21765f..193eb228d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,14 +4,36 @@ "version": "2.0.0", "tasks": [ { - "label": "Watch", + "label": "Watch CLJS", "type": "npm", - "script": "watch", - "problemMatcher": [], + "script": "watch-cljs", "group": { "kind": "build", "isDefault": true - } + }, + "problemMatcher": [] + }, + { + "label": "Watch TS", + "type": "npm", + "script": "watch-ts", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "Compile", + "type": "npm", + "script": "compile", + "problemMatcher": [] + }, + { + "label": "Release", + "type": "npm", + "script": "release", + "problemMatcher": [] } ] } \ No newline at end of file diff --git a/src/main/calva/connector.js b/calva/connector.ts similarity index 97% rename from src/main/calva/connector.js rename to calva/connector.ts index e7c62b238..ff16be542 100644 --- a/src/main/calva/connector.js +++ b/calva/connector.ts @@ -1,14 +1,14 @@ -import vscode from 'vscode'; -import _ from 'lodash'; -import fs from 'fs'; +import * as vscode from 'vscode'; +import * as _ from 'lodash'; +import * as fs from 'fs'; import * as state from './state'; import repl from './repl/client'; -import message from 'goog:calva.repl.message'; import * as util from './utilities'; import shadow from './shadow'; import status from './status'; import terminal from './terminal'; //const evaluate = require('./repl/middleware/evaluate'); +const { message } = require('../lib/calva'); function nreplPortFile() { if (fs.existsSync(shadow.shadowNReplPortFile())) { @@ -144,7 +144,8 @@ function makeCljsSessionClone(hostname, port, session, shadowBuild, callback) { let cljsSession = _.find(results, 'new-session')['new-session']; if (cljsSession) { let client = repl.create({ hostname, port }).once('connect', () => { - let msg = shadowBuild ? message.startShadowCljsReplMsg(cljsSession, shadowBuild) : message.startCljsReplMsg(cljsSession); + let msg = shadowBuild ? message.startShadowCljsReplMsg(cljsSession, shadowBuild) : + message.evalCode(cljsSession, util.getCljsReplStartCode()); client.send(msg, cljsResults => { client.end(); let valueResult = _.find(cljsResults, 'value'), diff --git a/src/main/calva/extension.js b/calva/extension.ts similarity index 94% rename from src/main/calva/extension.js rename to calva/extension.ts index 9df75444b..56dc143e8 100644 --- a/src/main/calva/extension.js +++ b/calva/extension.ts @@ -1,10 +1,9 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import * as state from './state'; import status from './status'; import connector from './connector'; -import greet from 'goog:calva.greet'; import terminal from './terminal'; -import CompletionItemProvider from './providers/completion'; +import CalvaCompletionItemProvider from './providers/completion'; import TextDocumentContentProvider from './providers/content'; import HoverProvider from './providers/hover'; import DefinitionProvider from './providers/definition'; @@ -13,6 +12,9 @@ import LintMiddleWare from './repl/middleware/lint'; import TestRunnerMiddleWare from './repl/middleware/testRunner'; import select from './repl/middleware/select'; +const { greetings } = require('../lib/calva'); + + function onDidSave(document) { let { evaluate, @@ -51,7 +53,8 @@ function activate(context) { let chan = state.deref().get('outputChannel'); chan.appendLine("Calva activated."); let { - autoConnect + autoConnect, + lint } = state.config(); status.update(); @@ -81,7 +84,7 @@ function activate(context) { context.subscriptions.push(vscode.commands.registerCommand('calva.evalCurrentTopLevelFormInREPLTerminal', terminal.evalCurrentTopLevelFormInREPLTerminalCommand)); // PROVIDERS - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(state.mode, new CompletionItemProvider())); + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(state.mode, new CalvaCompletionItemProvider())); context.subscriptions.push(vscode.languages.registerHoverProvider(state.mode, new HoverProvider())); context.subscriptions.push(vscode.languages.registerDefinitionProvider(state.mode, new DefinitionProvider())); @@ -95,7 +98,7 @@ function activate(context) { onDidSave(document); })); context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor((editor) => { - status.update(editor); + status.update(); })); context.subscriptions.push(new vscode.Disposable(() => { connector.disconnect(); @@ -108,7 +111,7 @@ function activate(context) { vscode.commands.executeCommand('setContext', 'calva:activated', true); - greet.activationGreetings(chan); + greetings.activationGreetings(chan, lint); //Try to connect using an existing .nrepl-port file, searching the root-directory if (autoConnect) { diff --git a/src/main/calva/providers/annotations.js b/calva/providers/annotations.ts similarity index 94% rename from src/main/calva/providers/annotations.js rename to calva/providers/annotations.ts index 795f8b386..65118a6c9 100644 --- a/src/main/calva/providers/annotations.js +++ b/calva/providers/annotations.ts @@ -1,4 +1,4 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; const evalResultsDecorationType = vscode.window.createTextEditorDecorationType({ before: { @@ -44,7 +44,7 @@ function clearEvaluationDecorations(editor) { function decorateResults(resultString, hasError, codeSelection, editor) { let decoration = evaluated(resultString, hasError) - decoration.range = new vscode.Selection(codeSelection.end, codeSelection.end); + decoration["range"] = new vscode.Selection(codeSelection.end, codeSelection.end); editor.setDecorations(evalResultsDecorationType, [decoration]); setTimeout(() => { let subscription = vscode.window.onDidChangeTextEditorSelection(() => { diff --git a/src/main/calva/providers/completion.js b/calva/providers/completion.ts similarity index 68% rename from src/main/calva/providers/completion.js rename to calva/providers/completion.ts index 3dc945342..0854ee6c0 100644 --- a/src/main/calva/providers/completion.js +++ b/calva/providers/completion.ts @@ -1,32 +1,35 @@ -import vscode from 'vscode'; +import { TextDocument, Position, CancellationToken, CompletionContext, Hover, CompletionItemKind, window, CompletionList, CompletionItemProvider, CompletionItem } from 'vscode'; import * as state from '../state'; import repl from '../repl/client'; -import message from 'goog:calva.repl.message'; import * as util from '../utilities'; +import { Context } from 'vm'; +const { message } = require('../../lib/calva'); -export default class CompletionItemProvider { +export default class CalvaCompletionItemProvider implements CompletionItemProvider { + state: any; + mappings: any; constructor() { this.state = state; this.mappings = { - 'nil': vscode.CompletionItemKind.Value, - 'macro': vscode.CompletionItemKind.Value, - 'class': vscode.CompletionItemKind.Class, - 'keyword': vscode.CompletionItemKind.Keyword, - 'namespace': vscode.CompletionItemKind.Module, - 'function': vscode.CompletionItemKind.Function, - 'special-form': vscode.CompletionItemKind.Keyword, - 'var': vscode.CompletionItemKind.Variable, - 'method': vscode.CompletionItemKind.Method + 'nil': CompletionItemKind.Value, + 'macro': CompletionItemKind.Value, + 'class': CompletionItemKind.Class, + 'keyword': CompletionItemKind.Keyword, + 'namespace': CompletionItemKind.Module, + 'function': CompletionItemKind.Function, + 'special-form': CompletionItemKind.Keyword, + 'var': CompletionItemKind.Variable, + 'method': CompletionItemKind.Method }; } - provideCompletionItems(document, position, _) { + provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext) { let text = util.getWordAtPosition(document, position), scope = this, filetypeIndex = (document.fileName.lastIndexOf('.') + 1), filetype = document.fileName.substr(filetypeIndex, document.fileName.length); if (this.state.deref().get("connected")) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let current = this.state.deref(), client = repl.create() .once('connect', () => { @@ -41,14 +44,14 @@ export default class CompletionItemProvider { let item = result.completions[c]; completions.push({ label: item.candidate, - kind: scope.mappings[item.type] || vscode.CompletionItemKind.Text, + kind: scope.mappings[item.type] || CompletionItemKind.Text, insertText: item[0] === '.' ? item.slice(1) : item }); } } } if (completions.length > 0) { - resolve(new vscode.CompletionList(completions, false)); + resolve(new CompletionList(completions, false)); } else { reject("No completions found"); } @@ -57,19 +60,19 @@ export default class CompletionItemProvider { }); }); } else { - return new vscode.Hover("Connect to repl for auto-complete.."); + return [new CompletionItem("Connect to repl for auto-complete..")]; } } - resolveCompletionItem(item, _) { - let editor = vscode.window.activeTextEditor, + resolveCompletionItem(item: CompletionItem, token: CancellationToken) { + let editor = window.activeTextEditor, filetypeIndex = (editor.document.fileName.lastIndexOf('.') + 1), filetype = editor.document.fileName.substr(filetypeIndex, editor.document.fileName.length); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let current = this.state.deref(); if (current.get('connected')) { let client = repl.create().once('connect', () => { - let document = vscode.window.activeTextEditor.document, + let document = window.activeTextEditor.document, msg = message.infoMsg(util.getSession(filetype), util.getNamespace(document.getText()), item.label); client.send(msg, function (results) { diff --git a/src/main/calva/providers/content.js b/calva/providers/content.ts similarity index 79% rename from src/main/calva/providers/content.js rename to calva/providers/content.ts index dc341d422..170b6a520 100644 --- a/src/main/calva/providers/content.js +++ b/calva/providers/content.ts @@ -1,9 +1,12 @@ +import * as vscode from 'vscode'; import * as state from '../state'; -import os from 'os'; -import fs from 'fs'; -import JSZip from 'jszip'; +import * as os from 'os'; +import * as fs from 'fs'; +import * as JSZip from 'jszip'; + +export default class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { + state: any; -export default class TextDocumentContentProvider { constructor() { this.state = state; } @@ -11,7 +14,7 @@ export default class TextDocumentContentProvider { provideTextDocumentContent(uri, token) { let current = this.state.deref(); if (current.get('connected')) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let rawPath = uri.path, pathToFileInJar = rawPath.slice(rawPath.search('!/') + 2), pathToJar = rawPath.slice('file:'.length); diff --git a/src/main/calva/providers/definition.js b/calva/providers/definition.ts similarity index 80% rename from src/main/calva/providers/definition.js rename to calva/providers/definition.ts index 2af63eb57..8479b715d 100644 --- a/src/main/calva/providers/definition.js +++ b/calva/providers/definition.ts @@ -1,10 +1,11 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import * as state from '../state'; import repl from '../repl/client'; -import message from 'goog:calva.repl.message'; import * as util from '../utilities'; +const { message } = require('../../lib/calva'); -export default class DefinitionProvider { +export default class DefinitionProvider implements vscode.DefinitionProvider { + state: any; constructor() { this.state = state; } @@ -15,8 +16,8 @@ export default class DefinitionProvider { scope = this, filetypeIndex = (document.fileName.lastIndexOf('.') + 1), filetype = document.fileName.substr(filetypeIndex, document.fileName.length); - if (this.state.deref().get('connected')) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + if (this.state.deref().get('connected')) { let current = scope.state.deref(), client = repl.create().once('connect', () => { let msg = message.infoMsg(util.getSession(filetype), @@ -37,9 +38,9 @@ export default class DefinitionProvider { client.end(); }); }); - }); - } else { - return new vscode.Hover("Not connected to nREPL.."); - } + } else { + reject("Not connected to a REPL…"); + } + }); } }; diff --git a/src/main/calva/providers/hover.js b/calva/providers/hover.ts similarity index 93% rename from src/main/calva/providers/hover.js rename to calva/providers/hover.ts index 0eedcf76a..bfaa0c853 100644 --- a/src/main/calva/providers/hover.js +++ b/calva/providers/hover.ts @@ -1,10 +1,12 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import * as state from '../state'; import repl from '../repl/client'; -import message from 'goog:calva.repl.message'; import * as util from '../utilities'; -export default class HoverProvider { +const { message } = require('../../lib/calva'); + +export default class HoverProvider implements vscode.HoverProvider { + state: any; constructor() { this.state = state; } @@ -45,7 +47,7 @@ export default class HoverProvider { } if (this.state.deref().get('connected')) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let current = this.state.deref(), client = repl.create() .once('connect', () => { diff --git a/src/main/calva/repl/client.js b/calva/repl/client.ts similarity index 87% rename from src/main/calva/repl/client.js rename to calva/repl/client.ts index f82ca3f5a..51c9963d7 100644 --- a/src/main/calva/repl/client.js +++ b/calva/repl/client.ts @@ -1,11 +1,11 @@ import * as state from '../state'; -import nrepl from 'goog:calva.repl.nrepl'; +const { nrepl } = require('../../lib/calva'); function send(msg, callback) { nrepl.message(this, msg, callback); } -function create(options) { +function create(options?) { let current = state.deref(), _options = null; if (current.get('connected')) { diff --git a/src/main/calva/repl/middleware/evaluate.js b/calva/repl/middleware/evaluate.ts similarity index 95% rename from src/main/calva/repl/middleware/evaluate.js rename to calva/repl/middleware/evaluate.ts index 4ed63f990..228be7efc 100644 --- a/src/main/calva/repl/middleware/evaluate.js +++ b/calva/repl/middleware/evaluate.ts @@ -1,12 +1,13 @@ -import vscode from 'vscode'; -import _ from 'lodash'; +import * as vscode from 'vscode'; +import * as _ from 'lodash'; import * as state from '../../state'; import repl from '../client'; -import message from 'goog:calva.repl.message'; import annotations from '../../providers/annotations'; import select from './select'; import * as util from '../../utilities'; +const { message } = require('../../../lib/calva'); + function evaluateMsg(msg, startStr, errorStr, callback) { let current = state.deref(), chan = current.get('outputChannel'); @@ -44,9 +45,9 @@ function evaluateSelection(document = {}, options = {}) { let current = state.deref(), chan = current.get('outputChannel'), doc = util.getDocument(document), - pprint = options.pprint || false, - replace = options.replace || false, - topLevel = options.topLevel || false, + pprint = options["pprint"] || false, + replace = options["replace"] || false, + topLevel = options["topLevel"] || false, session = util.getSession(util.getFileType(doc)); if (current.get('connected')) { diff --git a/src/main/calva/repl/middleware/lint.js b/calva/repl/middleware/lint.ts similarity index 95% rename from src/main/calva/repl/middleware/lint.js rename to calva/repl/middleware/lint.ts index 2c94ff578..c4def1a0c 100644 --- a/src/main/calva/repl/middleware/lint.js +++ b/calva/repl/middleware/lint.ts @@ -1,6 +1,6 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import { spawn } from 'child_process'; -import path from 'path'; +import * as path from 'path'; import * as state from '../../state'; import * as util from '../../utilities'; @@ -74,7 +74,7 @@ function lintDocument(document = {}) { } }); - joker.on("error", (error) => { + joker.on("error", (error: { message, code }) => { let { lint, jokerPath diff --git a/src/main/calva/repl/middleware/select.js b/calva/repl/middleware/select.ts similarity index 92% rename from src/main/calva/repl/middleware/select.js rename to calva/repl/middleware/select.ts index abd34eb6a..d4278dd23 100644 --- a/src/main/calva/repl/middleware/select.js +++ b/calva/repl/middleware/select.ts @@ -1,4 +1,4 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import * as util from '../../utilities'; const paredit = require('paredit.js'); @@ -10,7 +10,7 @@ function adjustRangeIgnoringComment(doc, range) { let start = doc.offsetAt(range.start), preTextLength = 0, end = doc.offsetAt(range.end), - postTextLength= 0, + postTextLength = 0, preMatch = text.match(/^\(\s*comment\s+/m), postMatch = text.match(/\s*\)\s*$/m); if (preMatch) { @@ -46,7 +46,7 @@ function selectCurrentForm(document = {}) { codeSelection = null; if (selection.isEmpty) { - codeSelection = getFormSelection(doc, selection.active); + codeSelection = getFormSelection(doc, selection.active, false); if (codeSelection) { editor.selection = codeSelection; } diff --git a/src/main/calva/repl/middleware/testRunner.js b/calva/repl/middleware/testRunner.ts similarity index 95% rename from src/main/calva/repl/middleware/testRunner.js rename to calva/repl/middleware/testRunner.ts index b70a0f268..412c65238 100644 --- a/src/main/calva/repl/middleware/testRunner.js +++ b/calva/repl/middleware/testRunner.ts @@ -1,17 +1,19 @@ -import vscode from 'vscode'; -import _ from 'lodash'; +import * as vscode from 'vscode'; +import * as _ from 'lodash'; import * as state from '../../state'; import repl from '../client'; -import message from 'goog:calva.repl.message'; import evaluate from './evaluate'; import * as util from '../../utilities'; +const { message } = require('../../../lib/calva'); + + let diagnosticCollection = vscode.languages.createDiagnosticCollection('calva'); function markTestResults(responsesArray, log = true) { let chan = state.deref().get('outputChannel'), diagnostics = {}, - total_summary = {}; + total_summary: { test, error, ns, var, fail } = { test: 0, error: 0, ns: 0, var: 0, fail: 0 }; diagnosticCollection.clear(); _.each(responsesArray, (responses) => { _.each(responses, response => { @@ -106,8 +108,8 @@ function runTests(messages, startStr, errorStr, log = true) { new Promise((resolve, reject) => { testClient = repl.create().once('connect', () => { testClient.send(messages[i], (result) => { - exceptions += _.some(result, "ex"); - errors += _.some(result, "err"); + exceptions += (_.some(result, "ex") ? 1 : 0); + errors += (_.some(result, "err") ? 1 : 0); if (!exceptions && !errors) { resolve(result); } else { diff --git a/src/main/calva/shadow.js b/calva/shadow.ts similarity index 92% rename from src/main/calva/shadow.js rename to calva/shadow.ts index b3bb38a92..cc1dec831 100644 --- a/src/main/calva/shadow.js +++ b/calva/shadow.ts @@ -1,5 +1,5 @@ -import fs from 'fs'; -import _ from 'lodash'; +import * as fs from 'fs'; +import * as _ from 'lodash'; import edn from 'jsedn'; import * as state from './state'; import * as util from './utilities'; diff --git a/src/main/calva/state.js b/calva/state.ts similarity index 80% rename from src/main/calva/state.js rename to calva/state.ts index 8f200b7cc..d15777b4f 100644 --- a/src/main/calva/state.js +++ b/calva/state.ts @@ -1,6 +1,6 @@ -import vscode from 'vscode'; -import Immutable from 'immutable'; -import ImmutableCursor from 'immutable-cursor'; +import * as vscode from 'vscode'; +import * as Immutable from 'immutable'; +import * as ImmutableCursor from 'immutable-cursor'; const mode = { language: 'clojure', @@ -35,7 +35,8 @@ function reset() { } function config() { - let configOptions = vscode.workspace.getConfiguration('calva'); + let configOptions = vscode.workspace.getConfiguration('calva'), + projectRootDirectoryConfig: string = configOptions.get("projectRootDirectory"); return { format: configOptions.get("formatOnSave"), evaluate: configOptions.get("evalOnSave"), @@ -43,7 +44,7 @@ function config() { test: configOptions.get("testOnSave"), autoConnect: configOptions.get("autoConnect"), connectREPLCommand: configOptions.get("connectREPLCommand"), - projectRootDirectory: configOptions.get("projectRootDirectory").replace(/^\/|\/$/g, ""), + projectRootDirectory: projectRootDirectoryConfig.replace(/^\/|\/$/g, ""), jokerPath: configOptions.get("jokerPath"), useJokerOnWSL: configOptions.get("useJokerOnWSL") }; diff --git a/src/main/calva/status.js b/calva/status.ts similarity index 100% rename from src/main/calva/status.js rename to calva/status.ts diff --git a/src/main/calva/statusbar.js b/calva/statusbar.ts similarity index 98% rename from src/main/calva/statusbar.js rename to calva/statusbar.ts index a0f08a3dd..4ae85cca0 100644 --- a/src/main/calva/statusbar.js +++ b/calva/statusbar.ts @@ -1,4 +1,4 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import * as state from './state'; import * as util from './utilities'; import shadow_util from './shadow'; diff --git a/src/main/calva/terminal.js b/calva/terminal.ts similarity index 99% rename from src/main/calva/terminal.js rename to calva/terminal.ts index ca8ae526c..ec259b7ec 100644 --- a/src/main/calva/terminal.js +++ b/calva/terminal.ts @@ -1,4 +1,4 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; import * as state from './state'; import * as util from './utilities'; import evaluate from './repl/middleware/evaluate'; diff --git a/src/main/calva/utilities.js b/calva/utilities.ts similarity index 98% rename from src/main/calva/utilities.js rename to calva/utilities.ts index 06c1ccbbc..8258ee756 100644 --- a/src/main/calva/utilities.js +++ b/calva/utilities.ts @@ -1,8 +1,8 @@ -import vscode from 'vscode'; +import * as vscode from 'vscode'; const specialWords = ['-', '+', '/', '*']; //TODO: Add more here -import _ from 'lodash'; +import * as _ from 'lodash'; import * as state from './state'; -import fs from 'fs'; +import * as fs from 'fs'; const syntaxQuoteSymbol = "`"; diff --git a/src/main/calva/greet.cljs b/lib-src/calva/greet.cljs similarity index 81% rename from src/main/calva/greet.cljs rename to lib-src/calva/greet.cljs index 878dec4c7..062b3825c 100644 --- a/src/main/calva/greet.cljs +++ b/lib-src/calva/greet.cljs @@ -1,15 +1,19 @@ (ns calva.greet - (:require ["/calva/state.js" :as state] - [clojure.string :as string])) + (:require [clojure.string :as string])) -(defn activationGreetings [chan] + +(defn activationGreetings [chan lint?] (.appendLine chan "Happy Clojure(script) coding! ❤️") (.appendLine chan "Please file any feature requests or bug reports here: https://github.com/BetterThanTomorrow/calva/issues") (.appendLine chan "I will also respond to any @pez mentions in the #editors channel of the Clojurians Slack: https://clojurians.slack.com/messages/editors/") (.appendLine chan "") (.appendLine chan "NOTE: Files are no longer automatically evaluated when opened. You will need to issue the evaluate file command at will instead.") (.appendLine chan "NOTE: The evaluate file command does not seem to work in shadow-cljs clojurescript repls. A workaround, that someetimes suffices, is to select all contents of the file and evaluate it.") - (when-not (.-lint (state/config)) + (when-not lint? (.appendLine chan "") (.appendLine chan "NOTE: Autolinting is disabled. You need to enable \"calva.lintOnSave\" in your editor settings to use it. But first install Joker: https://github.com/candid82/joker")) - (.appendLine chan (string/join (repeat 3 "-")))) \ No newline at end of file + (.appendLine chan (string/join (repeat 3 "-")))) + + +(def exports + (clj->js {:activationGreetings activationGreetings})) \ No newline at end of file diff --git a/lib-src/calva/migration.cljs b/lib-src/calva/migration.cljs new file mode 100644 index 000000000..ab003f444 --- /dev/null +++ b/lib-src/calva/migration.cljs @@ -0,0 +1,14 @@ +(ns calva.migration) + + +(defn jsify [o] + (clj->js o)) + + +(defn cljify [o] + (js->clj o :keywordize-keys true)) + + +(def exports + {:jsify jsify + :cljsify cljify}) \ No newline at end of file diff --git a/src/main/calva/repl/message.cljs b/lib-src/calva/repl/message.cljs similarity index 75% rename from src/main/calva/repl/message.cljs rename to lib-src/calva/repl/message.cljs index 1360a9b80..ba806c637 100644 --- a/src/main/calva/repl/message.cljs +++ b/lib-src/calva/repl/message.cljs @@ -1,5 +1,9 @@ -(ns calva.repl.message - (:require ["/calva/utilities.js" :as util])) +(ns calva.repl.message) + + +(defn shadow-cljs-repl-start-code [build] + (str "(shadow.cljs.devtools.api/nrepl-select " build ")")) + (def operation {:EVALUATE "eval" @@ -19,14 +23,14 @@ :RETEST "retest" :PPRINT "pprint"}) -(defn startCljsReplMsg [session] +(defn eval-code-msg [session code] {:op (operation :EVALUATE) - :code (util/getCljsReplStartCode) + :code code :session session}) (defn startShadowCljsReplMsg [session build] {:op (operation :EVALUATE) - :code (util/getShadowCljsReplStartCode build) + :code (shadow-cljs-repl-start-code build) :session session}) (defn listSessionsMsg [] @@ -109,21 +113,24 @@ :session session}) (def message - {:evaluate evaluateMsg - :listSessions listSessionsMsg - :loadFile loadFileMsg - :complete completeMsg - :info infoMsg - :stacktrace stacktraceMsg - :clone cloneMsg - :close closeMsg - :refresh refreshMsg - :refreshAll refreshAllMsg - :refreshClear refreshClearMsg - :test testMsg - :testAll testAllMsg - :rerunTests rerunTestsMsg - :format formatMsg + {:evaluateMsg evaluateMsg + :listSessionsMsg listSessionsMsg + :loadFileMsg loadFileMsg + :completeMsg completeMsg + :infoMsg infoMsg + :stacktraceMsg stacktraceMsg + :cloneMsg cloneMsg + :closeMsg closeMsg + :refreshMsg refreshMsg + :refreshAllMsg refreshAllMsg + :refreshClearMsg refreshClearMsg + :testMsg testMsg + :testAllMsg testAllMsg + :rerunTestsMsg rerunTestsMsg + :formatMsg formatMsg :operation operation - :startCljsRepl startCljsReplMsg - :startShadowCljsRepl startShadowCljsReplMsg}) \ No newline at end of file + :evalCode eval-code-msg + :startShadowCljsReplMsg startShadowCljsReplMsg}) + +(def exports + (clj->js message)) \ No newline at end of file diff --git a/src/main/calva/repl/nrepl.cljs b/lib-src/calva/repl/nrepl.cljs similarity index 98% rename from src/main/calva/repl/nrepl.cljs rename to lib-src/calva/repl/nrepl.cljs index 9ce3fd751..6b0908f01 100644 --- a/src/main/calva/repl/nrepl.cljs +++ b/lib-src/calva/repl/nrepl.cljs @@ -86,3 +86,9 @@ (callback (clj->js @*state)))))) (.write conn (bencoder/encode (clj->js msg)) "binary"))) + + +(def exports + (clj->js + {:connect connect + :message message})) \ No newline at end of file diff --git a/lib/calva.js b/lib/calva.js new file mode 100644 index 000000000..af8a43ace --- /dev/null +++ b/lib/calva.js @@ -0,0 +1,3059 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof module === 'object' && module.exports) { + module.exports = factory(); + } else { + root.returnExports = factory(); + } +})(this, function () { + +var SHADOW_IMPORT_PATH = "/Users/petery/Projects/clojure4vscode/.shadow-cljs/builds/calva/dev/out/cljs-runtime"; +global.$CLJS = global; +try {require('source-map-support').install();} catch (e) {console.warn('no "source-map-support" (run "npm install source-map-support --save-dev" to get it)');} + +global.CLOSURE_NO_DEPS = true; + +global.CLOSURE_DEFINES = {"shadow.cljs.devtools.client.env.repl_pprint":false,"shadow.cljs.devtools.client.env.devtools_url":"","shadow.cljs.devtools.client.env.autoload":false,"shadow.cljs.devtools.client.env.proc_id":"dc7f55e0-c7aa-4980-ab1f-151dc1d0b1f2","goog.ENABLE_DEBUG_LOADER":false,"shadow.cljs.devtools.client.env.server_port":9630,"shadow.cljs.devtools.client.env.use_document_host":true,"shadow.cljs.devtools.client.env.module_format":"goog","goog.LOCALE":"en","shadow.cljs.devtools.client.env.build_id":"calva","shadow.cljs.devtools.client.env.ignore_warnings":false,"goog.DEBUG":true,"cljs.core._STAR_target_STAR_":"nodejs","shadow.cljs.devtools.client.env.ssl":false,"shadow.cljs.devtools.client.env.enabled":true,"shadow.cljs.devtools.client.env.server_host":"localhost","goog.TRANSPILE":"never"}; + +var goog = global.goog = {}; + +var SHADOW_IMPORTED = global.SHADOW_IMPORTED = {}; +var PATH = require("path"); +var VM = require("vm"); +var FS = require("fs"); + +var SHADOW_PROVIDE = function(name) { + return goog.exportPath_(name, undefined); +}; + +var SHADOW_REQUIRE = function(name) { + return true; +}; + +var SHADOW_WRAP = function(js) { + var code = "(function (require, module, __filename, __dirname) {\n"; + // this is part of goog/base.js and for some reason the only global var not on goog or goog.global + code += "var COMPILED = false;\n" + code += js; + code += "\n});"; + return code; +}; + +var SHADOW_IMPORT = global.SHADOW_IMPORT = function(src) { + if (CLOSURE_DEFINES["shadow.debug"]) { + console.info("SHADOW load:", src); + } + + SHADOW_IMPORTED[src] = true; + + // SHADOW_IMPORT_PATH is an absolute path + var filePath = PATH.resolve(SHADOW_IMPORT_PATH, src); + + var js = FS.readFileSync(filePath); + + var code = SHADOW_WRAP(js); + + var fn = VM.runInThisContext(code, + {filename: filePath, + lineOffset: -2, // see SHADOW_WRAP, adds 2 lines + displayErrors: true + }); + + // the comment is for source-map-support which unfortunately shows the wrong piece of code but the stack is correct + try { + /* ignore this, look at stacktrace */ fn.call(global, require, module, __filename, __dirname); + } catch (e) { + console.error("SHADOW import error", filePath); + throw e; + } + + return true; +}; + +global.SHADOW_NODE_EVAL = function(js, smJson) { + if (smJson) { + js += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,"; + js += new Buffer(smJson).toString('base64'); + } + + // console.log(js); + + return VM.runInThisContext.call(global, js, + {filename: "", + lineOffset: 0, + displayErrors: true}); +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Bootstrap for the Google JS Library (Closure). + * + * In uncompiled mode base.js will attempt to load Closure's deps file, unless + * the global CLOSURE_NO_DEPS is set to true. This allows projects + * to include their own deps file(s) from different locations. + * + * Avoid including base.js more than once. This is strictly discouraged and not + * supported. goog.require(...) won't work properly in that case. + * + * @provideGoog + */ + + +/** + * @define {boolean} Overridden to true by the compiler. + */ +var COMPILED = false; + + +/** + * Base namespace for the Closure library. Checks to see goog is already + * defined in the current scope before assigning to prevent clobbering if + * base.js is loaded more than once. + * + * @const + */ +var goog = goog || {}; + + +/** + * Reference to the global context. In most cases this will be 'window'. + */ +goog.global = global; + + +/** + * A hook for overriding the define values in uncompiled mode. + * + * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before + * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, + * {@code goog.define} will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). + * + * Example: + *
+ *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
+ *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_DEFINES; + + +/** + * Returns true if the specified value is not undefined. + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined. + */ +goog.isDef = function(val) { + // void 0 always evaluates to undefined and hence we do not need to depend on + // the definition of the global variable named 'undefined'. + return val !== void 0; +}; + +/** + * Returns true if the specified value is a string. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a string. + */ +goog.isString = function(val) { + return typeof val == 'string'; +}; + + +/** + * Returns true if the specified value is a boolean. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is boolean. + */ +goog.isBoolean = function(val) { + return typeof val == 'boolean'; +}; + + +/** + * Returns true if the specified value is a number. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a number. + */ +goog.isNumber = function(val) { + return typeof val == 'number'; +}; + + +/** + * Builds an object structure for the provided namespace path, ensuring that + * names that already exist are not overwritten. For example: + * "a.b.c" -> a = {};a.b={};a.b.c={}; + * Used by goog.provide and goog.exportSymbol. + * @param {string} name name of the object that this file defines. + * @param {*=} opt_object the object to expose at the end of the path. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is `goog.global`. + * @private + */ +goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { + var parts = name.split('.'); + var cur = opt_objectToExportTo || goog.global; + + // Internet Explorer exhibits strange behavior when throwing errors from + // methods externed in this manner. See the testExportSymbolExceptions in + // base_test.html for an example. + if (!(parts[0] in cur) && cur.execScript) { + cur.execScript('var ' + parts[0]); + } + + for (var part; parts.length && (part = parts.shift());) { + if (!parts.length && goog.isDef(opt_object)) { + // last part and we have an object; use it + cur[part] = opt_object; + } else if (cur[part] && cur[part] !== Object.prototype[part]) { + cur = cur[part]; + } else { + cur = cur[part] = {}; + } + } +}; + + +/** + * Defines a named value. In uncompiled mode, the value is retrieved from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler + * options or the value set in the CLOSURE_DEFINES object. + * + * @param {string} name The distinguished name to provide. + * @param {string|number|boolean} defaultValue + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + if (goog.global.CLOSURE_UNCOMPILED_DEFINES && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (goog.global.CLOSURE_UNCOMPILED_DEFINES).nodeType === + undefined && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { + value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; + } else if ( + goog.global.CLOSURE_DEFINES && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (goog.global.CLOSURE_DEFINES).nodeType === undefined && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_DEFINES, name)) { + value = goog.global.CLOSURE_DEFINES[name]; + } + } + goog.exportPath_(name, value); +}; + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production. It can be easily stripped + * by specifying --define goog.DEBUG=false to the Closure Compiler aka + * JSCompiler. For example, most toString() methods should be declared inside an + * "if (goog.DEBUG)" conditional because they are generally used for debugging + * purposes and it is difficult for the JSCompiler to statically determine + * whether they are used. + */ +goog.define('goog.DEBUG', true); + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=" as a compiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he). + * + */ +goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the compiler. + */ +goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether a project is expected to be running in strict mode. + * + * This define can be used to trigger alternate implementations compatible with + * running in EcmaScript Strict mode or warn about unavailable functionality. + * @see https://goo.gl/PudQ4y + * + */ +goog.define('goog.STRICT_MODE_COMPATIBLE', false); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * @define {boolean} Whether to use a Chrome app CSP-compliant method for + * loading scripts via goog.require. @see appendScriptSrcNode_. + */ +goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); + + +/** + * Defines a namespace in Closure. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * The presence of one or more goog.provide() calls in a file indicates + * that the file defines the given objects/namespaces. + * Provided symbols must not be null or undefined. + * + * In addition, goog.provide() creates the object stubs for a namespace + * (for example, goog.provide("goog.foo.bar") will create the object + * goog.foo.bar if it does not already exist). + * + * Build tools also scan for provide/require/module statements + * to discern dependencies, build dependency files (see deps.js), etc. + * + * @see goog.require + * @see goog.module + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + */ +goog.provide = function(name) { + if (goog.isInModuleLoader_()) { + throw Error('goog.provide can not be used within a goog.module.'); + } + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + } + + goog.constructNamespace_(name); +}; + + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {Object=} opt_obj The object to embed in the namespace. + * @private + */ +goog.constructNamespace_ = function(name, opt_obj) { + if (!COMPILED) { + delete goog.implicitNamespaces_[name]; + + var namespace = name; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, opt_obj); +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lenient, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + * @return {void} + */ +goog.module = function(name) { + if (!goog.isString(name) || !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw Error('Invalid module identifier'); + } + if (!goog.isInModuleLoader_()) { + throw Error( + 'Module ' + name + ' has been loaded incorrectly. Note, ' + + 'modules cannot be loaded as normal scripts. They require some kind of ' + + 'pre-processing step. You\'re likely trying to load a module via a ' + + 'script tag or as a part of a concatenated bundle without rewriting the ' + + 'module. For more info see: ' + + 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (name in goog.loadedModules_) { + return goog.loadedModules_[name]; + } else if (!goog.implicitNamespaces_[name]) { + var ns = goog.getObjectByName(name); + return ns != null ? ns : null; + } + } + return null; +}; + + +/** + * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}} + */ +goog.moduleLoaderState_ = null; + + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInModuleLoader_ = function() { + return goog.moduleLoaderState_ != null; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInModuleLoader_()) { + throw new Error( + 'goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error( + 'goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw Error( + 'Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * Before using goog.forwardDeclare, please read the documentation at + * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to + * understand the options and tradeoffs when working with forward declarations. + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + */ +goog.forwardDeclare = function(name) {}; + + +/** + * Forward declare type information. Used to assign types to goog.global + * referenced object that would otherwise result in unknown type references + * and thus block property disambiguation. + */ +goog.forwardDeclare('Document'); +goog.forwardDeclare('HTMLScriptElement'); +goog.forwardDeclare('XMLHttpRequest'); + + +if (!COMPILED) { + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && + goog.isDefAndNotNull(goog.getObjectByName(name))); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} + + +/** + * Returns an object based on its fully qualified external name. The object + * is not found if null or undefined. If you are using a compilation pass that + * renames property names beware that using this function will not find renamed + * properties. + * + * @param {string} name The fully qualified name. + * @param {Object=} opt_obj The object within which to look; default is + * |goog.global|. + * @return {?} The value (object or primitive) or, if not found, null. + */ +goog.getObjectByName = function(name, opt_obj) { + var parts = name.split('.'); + var cur = opt_obj || goog.global; + for (var part; part = parts.shift();) { + if (goog.isDefAndNotNull(cur[part])) { + cur = cur[part]; + } else { + return null; + } + } + return cur; +}; + + +/** + * Globalizes a whole namespace, such as goog or goog.lang. + * + * @param {!Object} obj The namespace to globalize. + * @param {Object=} opt_global The object to add the properties to. + * @deprecated Properties may be explicitly exported to the global scope, but + * this should no longer be done in bulk. + */ +goog.globalize = function(obj, opt_global) { + var global = opt_global || goog.global; + for (var x in obj) { + global[x] = obj[x]; + } +}; + + +/** + * Adds a dependency from a file to the files it requires. + * @param {string} relPath The path to the js file. + * @param {!Array} provides An array of strings with + * the names of the objects this file provides. + * @param {!Array} requires An array of strings with + * the names of the objects this file requires. + * @param {boolean|!Object=} opt_loadFlags Parameters indicating + * how the file must be loaded. The boolean 'true' is equivalent + * to {'module': 'goog'} for backwards-compatibility. Valid properties + * and values include {'module': 'goog'} and {'lang': 'es6'}. + */ +goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { + if (goog.DEPENDENCIES_ENABLED) { + var provide, require; + var path = relPath.replace(/\\/g, '/'); + var deps = goog.dependencies_; + if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') { + opt_loadFlags = opt_loadFlags ? {'module': 'goog'} : {}; + } + for (var i = 0; provide = provides[i]; i++) { + deps.nameToPath[provide] = path; + deps.loadFlags[path] = opt_loadFlags; + } + for (var j = 0; require = requires[j]; j++) { + if (!(path in deps.requires)) { + deps.requires[path] = {}; + } + deps.requires[path][require] = true; + } + } +}; + + + + +// NOTE(nnaze): The debug DOM loader was included in base.js as an original way +// to do "debug-mode" development. The dependency system can sometimes be +// confusing, as can the debug DOM loader's asynchronous nature. +// +// With the DOM loader, a call to goog.require() is not blocking -- the script +// will not load until some point after the current script. If a namespace is +// needed at runtime, it needs to be defined in a previous script, or loaded via +// require() with its registered dependencies. +// +// User-defined namespaces may need their own deps file. For a reference on +// creating a deps file, see: +// Externally: https://developers.google.com/closure/library/docs/depswriter +// +// Because of legacy clients, the DOM loader can't be easily removed from +// base.js. Work was done to make it disableable or replaceable for +// different environments (DOM-less JavaScript interpreters like Rhino or V8, +// for example). See bootstrap/ for more information. + + +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + +/** + * Implements a system for the dynamic resolution of dependencies that works in + * parallel with the BUILD system. Note that all calls to goog.require will be + * stripped by the compiler. + * @see goog.provide + * @param {string} name Namespace to include (as was given in goog.provide()) in + * the form "goog.package.part". + * @return {?} If called within a goog.module file, the associated namespace or + * module otherwise null. + */ +goog.require = function(name) { + // If the object already exists we do not need to do anything. + if (!COMPILED) { + if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) { + goog.maybeProcessDeferredDep_(name); + } + + if (goog.isProvided_(name)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(name); + } + } else if (goog.ENABLE_DEBUG_LOADER) { + var path = goog.getPathFromDeps_(name); + if (path) { + goog.writeScripts_(path); + } else { + var errorMessage = 'goog.require could not find: ' + name; + goog.logToConsole_(errorMessage); + + throw Error(errorMessage); + } + } + + return null; + } +}; + + +/** + * Path for included scripts. + * @type {string} + */ +goog.basePath = ''; + + +/** + * A hook for overriding the base path. + * @type {string|undefined} + */ +goog.global.CLOSURE_BASE_PATH; + + +/** + * Whether to attempt to load Closure's deps file. By default, when uncompiled, + * deps files will attempt to be loaded. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; + + +/** + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The function is passed the script source, which is a relative URI. It should + * return true if the script was imported, false otherwise. + * @type {(function(string): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + */ +goog.nullFunction = function() {}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + */ +goog.abstractMethod = function() { + throw Error('unimplemented abstract method'); +}; + + +/** + * Adds a {@code getInstance} static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + */ +goog.addSingletonGetter = function(ctor) { + // instance_ is immediately set to prevent issues with sealed constructors + // such as are encountered when a constructor is returned as the export object + // of a goog.module in unoptimized code. + ctor.instance_ = undefined; + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + return ctor.instance_ = new ctor; + }; +}; + + +/** + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the {@code goog.testing.singleton} module. The compiler + * removes this variable if unused. + * @type {!Array} + * @private + */ +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using {@code eval} when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or similar. + * However in some environments the use of {@code eval} is banned + * so we provide an alternative. + */ +goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * the module identifier to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + +/** + * True if goog.dependencies_ is available. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +/** + * @define {string} How to decide whether to transpile. Valid values + * are 'always', 'never', and 'detect'. The default ('detect') is to + * use feature detection to determine which language levels need + * transpilation. + */ +// NOTE(user): we could expand this to accept a language level to bypass +// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but +// would leave ES3 and ES5 files alone. +goog.define('goog.TRANSPILE', 'detect'); + + +/** + * @define {string} Path to the transpiler. Executing the script at this + * path (relative to base.js) should define a function $jscomp.transpile. + */ +goog.define('goog.TRANSPILER', 'transpile.js'); + + +if (goog.DEPENDENCIES_ENABLED) { + /** + * This object is used to keep track of dependencies and other data that is + * used for loading scripts. + * @private + * @type {{ + * loadFlags: !Object>, + * nameToPath: !Object, + * requires: !Object>, + * visited: !Object, + * written: !Object, + * deferred: !Object + * }} + */ + goog.dependencies_ = { + loadFlags: {}, // 1 to 1 + + nameToPath: {}, // 1 to 1 + + requires: {}, // 1 to many + + // Used when resolving dependencies to prevent us from visiting file twice. + visited: {}, + + written: {}, // Used to keep track of script files we have written. + + deferred: {} // Used to track deferred module evaluations in old IEs + }; + + + /** + * Tries to detect whether is in the context of an HTML document. + * @return {boolean} True if it looks like HTML document. + * @private + */ + goog.inHtmlDocument_ = function() { + /** @type {Document} */ + var doc = goog.global.document; + return doc != null && 'write' in doc; // XULDocument misses write. + }; + + + /** + * Tries to detect the base path of base.js script that bootstraps Closure. + * @private + */ + goog.findBasePath_ = function() { + if (goog.isDef(goog.global.CLOSURE_BASE_PATH) && + // Anti DOM-clobbering runtime check (b/37736576). + goog.isString(goog.global.CLOSURE_BASE_PATH)) { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + return; + } else if (!goog.inHtmlDocument_()) { + return; + } + /** @type {Document} */ + var doc = goog.global.document; + // If we have a currentScript available, use it exclusively. + var currentScript = doc.currentScript; + if (currentScript) { + var scripts = [currentScript]; + } else { + var scripts = doc.getElementsByTagName('SCRIPT'); + } + // Search backwards since the current script is in almost all cases the one + // that has base.js. + for (var i = scripts.length - 1; i >= 0; --i) { + var script = /** @type {!HTMLScriptElement} */ (scripts[i]); + var src = script.src; + var qmark = src.lastIndexOf('?'); + var l = qmark == -1 ? src.length : qmark; + if (src.substr(l - 7, 7) == 'base.js') { + goog.basePath = src.substr(0, l - 7); + return; + } + } + }; + + + /** + * Imports a script if, and only if, that script hasn't already been imported. + * (Must be called at execution time) + * @param {string} src Script source. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @private + */ + goog.importScript_ = function(src, opt_sourceText) { + var importScript = + goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; + if (importScript(src, opt_sourceText)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** + * Whether the browser is IE9 or earlier, which needs special handling + * for deferred modules. + * @const @private {boolean} + */ + goog.IS_OLD_IE_ = + !!(!goog.global.atob && goog.global.document && goog.global.document.all); + + + /** + * Whether IE9 or earlier is waiting on a dependency. This ensures that + * deferred modules that have no non-deferred dependencies actually get + * loaded, since if we defer them and then never pull in a non-deferred + * script, then `goog.loadQueuedModules_` will never be called. Instead, + * if not waiting on anything we simply don't defer in the first place. + * @private {boolean} + */ + goog.oldIeWaiting_ = false; + + + /** + * Given a URL initiate retrieval and execution of a script that needs + * pre-processing. + * @param {string} src Script source URL. + * @param {boolean} isModule Whether this is a goog.module. + * @param {boolean} needsTranspile Whether this source needs transpilation. + * @private + */ + goog.importProcessedScript_ = function(src, isModule, needsTranspile) { + // In an attempt to keep browsers from timing out loading scripts using + // synchronous XHRs, put each load in its own script block. + var bootstrap = 'goog.retrieveAndExec_("' + src + '", ' + isModule + ', ' + + needsTranspile + ');'; + + goog.importScript_('', bootstrap); + }; + + + /** @private {!Array} */ + goog.queuedModules_ = []; + + + /** + * Return an appropriate module text. Suitable to insert into + * a script tag (that is unescaped). + * @param {string} srcUrl + * @param {string} scriptText + * @return {string} + * @private + */ + goog.wrapModule_ = function(srcUrl, scriptText) { + if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) { + return '' + + 'goog.loadModule(function(exports) {' + + '"use strict";' + scriptText + + '\n' + // terminate any trailing single line comment. + ';return exports' + + '});' + + '\n//# sourceURL=' + srcUrl + '\n'; + } else { + return '' + + 'goog.loadModule(' + + goog.global.JSON.stringify( + scriptText + '\n//# sourceURL=' + srcUrl + '\n') + + ');'; + } + }; + + // On IE9 and earlier, it is necessary to handle + // deferred module loads. In later browsers, the + // code to be evaluated is simply inserted as a script + // block in the correct order. To eval deferred + // code at the right time, we piggy back on goog.require to call + // goog.maybeProcessDeferredDep_. + // + // The goog.requires are used both to bootstrap + // the loading process (when no deps are available) and + // declare that they should be available. + // + // Here we eval the sources, if all the deps are available + // either already eval'd or goog.require'd. This will + // be the case when all the dependencies have already + // been loaded, and the dependent module is loaded. + // + // But this alone isn't sufficient because it is also + // necessary to handle the case where there is no root + // that is not deferred. For that there we register for an event + // and trigger goog.loadQueuedModules_ handle any remaining deferred + // evaluations. + + /** + * Handle any remaining deferred goog.module evals. + * @private + */ + goog.loadQueuedModules_ = function() { + var count = goog.queuedModules_.length; + if (count > 0) { + var queue = goog.queuedModules_; + goog.queuedModules_ = []; + for (var i = 0; i < count; i++) { + var path = queue[i]; + goog.maybeProcessDeferredPath_(path); + } + } + goog.oldIeWaiting_ = false; + }; + + + /** + * Eval the named module if its dependencies are + * available. + * @param {string} name The module to load. + * @private + */ + goog.maybeProcessDeferredDep_ = function(name) { + if (goog.isDeferredModule_(name) && goog.allDepsAreAvailable_(name)) { + var path = goog.getPathFromDeps_(name); + goog.maybeProcessDeferredPath_(goog.basePath + path); + } + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose evaluation has been deferred. + * @private + */ + goog.isDeferredModule_ = function(name) { + var path = goog.getPathFromDeps_(name); + var loadFlags = path && goog.dependencies_.loadFlags[path] || {}; + var languageLevel = loadFlags['lang'] || 'es3'; + if (path && (loadFlags['module'] == 'goog' || + goog.needsTranspile_(languageLevel))) { + var abspath = goog.basePath + path; + return (abspath) in goog.dependencies_.deferred; + } + return false; + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose declared dependencies have all been loaded + * (eval'd or a deferred module load) + * @private + */ + goog.allDepsAreAvailable_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && (path in goog.dependencies_.requires)) { + for (var requireName in goog.dependencies_.requires[path]) { + if (!goog.isProvided_(requireName) && + !goog.isDeferredModule_(requireName)) { + return false; + } + } + } + return true; + }; + + + /** + * @param {string} abspath + * @private + */ + goog.maybeProcessDeferredPath_ = function(abspath) { + if (abspath in goog.dependencies_.deferred) { + var src = goog.dependencies_.deferred[abspath]; + delete goog.dependencies_.deferred[abspath]; + goog.globalEval(src); + } + }; + + + /** + * Load a goog.module from the provided URL. This is not a general purpose + * code loader and does not support late loading code, that is it should only + * be used during page load. This method exists to support unit tests and + * "debug" loaders that would otherwise have inserted script tags. Under the + * hood this needs to use a synchronous XHR and is not recommeneded for + * production code. + * + * The module's goog.requires must have already been satisified; an exception + * will be thrown if this is not the case. This assumption is that no + * "deps.js" file exists, so there is no way to discover and locate the + * module-to-be-loaded's dependencies and no attempt is made to do so. + * + * There should only be one attempt to load a module. If + * "goog.loadModuleFromUrl" is called for an already loaded module, an + * exception will be throw. + * + * @param {string} url The URL from which to attempt to load the goog.module. + */ + goog.loadModuleFromUrl = function(url) { + // Because this executes synchronously, we don't need to do any additional + // bookkeeping. When "goog.loadModule" the namespace will be marked as + // having been provided which is sufficient. + goog.retrieveAndExec_(url, true, false); + }; + + + /** + * Writes a new script pointing to {@code src} directly into the DOM. + * + * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for + * the fallback mechanism. + * + * @param {string} src The script URL. + * @private + */ + goog.writeScriptSrcNode_ = function(src) { + goog.global.document.write( + '