From 67efbf07047414779db9f5d6ecfd42cb21278dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Nuno=20Monteiro?= Date: Tue, 18 Apr 2017 16:55:08 -0700 Subject: [PATCH] Dynamic classpath (#133) --- .eslintrc | 3 +- CHANGELOG.md | 6 ++- scripts/aot-bundle-macros.bat | 4 +- scripts/aot-bundle-macros.sh | 3 ++ src/cljs/bundled/lumo/classpath.cljs | 56 ++++++++++++++++++++++++++++ src/cljs/snapshot/lumo/repl.cljs | 2 + src/js/__tests__/lumo-test.js | 5 ++- src/js/cljs.js | 6 ++- src/js/lumo.js | 32 +++++++--------- 9 files changed, 90 insertions(+), 27 deletions(-) create mode 100644 src/cljs/bundled/lumo/classpath.cljs diff --git a/.eslintrc b/.eslintrc index c63c8b3c..73f54e3c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,11 +13,10 @@ "plugins" : ["flowtype", "import", "babel"], "rules" : { "linebreak-style": 0, - "no-duplicate-imports": 0, + "no-restricted-syntax": 0, "no-unused-vars": ["error", { "args": "none" }], "no-cond-assign": [2, "except-parens"], "keyword-spacing": [2, {"before": true, "after": true}], - "array-bracket-spacing": 0, "babel/array-bracket-spacing": 2, "import/no-duplicates": 2, "flowtype/boolean-style": 2, diff --git a/CHANGELOG.md b/CHANGELOG.md index e21ef503..37c7ff37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ turn off pretty printing by binding `lumo.repl/*pprint-results*` to `false`. ([#127](https://github.com/anmonteiro/lumo/issues/127)). - Interrupt printing at the REPL with `Ctrl+C` ([#107](https://github.com/anmonteiro/lumo/issues/107)) - Add missing Clojure repl special vars (*1, *2, *3, *e) ([#101](https://github.com/anmonteiro/lumo/issues/101)) +- Make the classpath dynamic, add a new namespace `lumo.classpath` to interact +with the Lumo classpath ([#31](https://github.com/anmonteiro/lumo/issues/31)). ### Changes @@ -20,9 +22,9 @@ turn off pretty printing by binding `lumo.repl/*pprint-results*` to `false`. - Do not print empty line if input is empty or whitespace only (make behavior consistent with `clojure.main`). - Invalidate cache when source file changes ([#54](https://github.com/anmonteiro/lumo/issues/54)). -- Upgrade ClojureScript to version 1.9.518. +- Upgrade ClojureScript to version 1.9.521. - Upgrade Closure Compiler JS to version v20170409. -- Upgrade to Node.js version 7.9.0. +- Upgrade Node.js to version 7.9.0. ### Bug fixes diff --git a/scripts/aot-bundle-macros.bat b/scripts/aot-bundle-macros.bat index 6fa311b2..70c4d851 100644 --- a/scripts/aot-bundle-macros.bat +++ b/scripts/aot-bundle-macros.bat @@ -11,7 +11,7 @@ echo "### Compiling Macro Namespaces" mkdir lumo-cljs\out\macros-tmp || goto :error -echo (require 'lumo.build.api 'lumo.analyzer 'lumo.cljs-deps 'lumo.closure 'lumo.compiler 'lumo.io 'lumo.json 'lumo.util 'clojure.core.reducers 'clojure.zip 'clojure.data 'cljs.nodejs 'cljs.pprint 'cljs.test 'cljs.analyzer.api) (require-macros 'lumo.repl 'lumo.util 'clojure.template 'cljs.pprint 'cljs.support 'cljs.spec 'cljs.spec.impl.gen 'cljs.test 'cljs.reader 'cljs.env.macros 'cljs.analyzer.macros 'cljs.compiler.macros) | build\lumo.exe --quiet -c target -sdk lumo-cljs/out/macros-tmp || goto :error +echo (require 'lumo.build.api 'lumo.analyzer 'lumo.cljs-deps 'lumo.classpath 'lumo.closure 'lumo.compiler 'lumo.io 'lumo.json 'lumo.util 'clojure.core.reducers 'clojure.zip 'clojure.data 'cljs.nodejs 'cljs.pprint 'cljs.test 'cljs.analyzer.api) (require-macros 'lumo.repl 'lumo.util 'clojure.template 'cljs.pprint 'cljs.support 'cljs.spec 'cljs.spec.impl.gen 'cljs.test 'cljs.reader 'cljs.env.macros 'cljs.analyzer.macros 'cljs.compiler.macros) | build\lumo.exe --quiet -c target -sdk lumo-cljs/out/macros-tmp || goto :error mv lumo-cljs\out\macros-tmp\clojure_SLASH_core_SLASH_reducers.js target\clojure\core\reducers.js || goto :error mv lumo-cljs\out\macros-tmp\clojure_SLASH_core_SLASH_reducers.cache.json target\clojure\core\reducers.cache.json || goto :error @@ -33,6 +33,8 @@ mv lumo-cljs\out\macros-tmp\lumo_SLASH_build_SLASH_api.js target\lumo\build\api. mv lumo-cljs\out\macros-tmp\lumo_SLASH_build_SLASH_api.cache.json target\lumo\build\api.cache.json || goto :error mv lumo-cljs\out\macros-tmp\lumo_SLASH_cljs_deps.js target\lumo\cljs_deps.js || goto :error mv lumo-cljs\out\macros-tmp\lumo_SLASH_cljs_deps.cache.json target\lumo\cljs_deps.cache.json || goto :error +mv lumo-cljs\out\macros-tmp\lumo_SLASH_classpath.js target\lumo\classpath.js || goto :error +mv lumo-cljs\out\macros-tmp\lumo_SLASH_classpath.cache.json target\lumo\classpath.cache.json || goto :error mv lumo-cljs\out\macros-tmp\lumo_SLASH_closure.js target\lumo\closure.js || goto :error mv lumo-cljs\out\macros-tmp\lumo_SLASH_closure.cache.json target\lumo\closure.cache.json || goto :error mv lumo-cljs\out\macros-tmp\lumo_SLASH_compiler.js target\lumo\compiler.js || goto :error diff --git a/scripts/aot-bundle-macros.sh b/scripts/aot-bundle-macros.sh index d3a6bc6b..18fa8c3e 100755 --- a/scripts/aot-bundle-macros.sh +++ b/scripts/aot-bundle-macros.sh @@ -17,6 +17,7 @@ $(pwd)/build/lumo --quiet -c target -sdk lumo-cljs/out/macros-tmp < path-or-paths + (not (sequential? path-or-paths)) vector)))) + +(defn remove! + "Remove a directory or JAR file from the Lumo classpath." + [path] + (js/$$LUMO_GLOBALS.removeSourcePath path)) diff --git a/src/cljs/snapshot/lumo/repl.cljs b/src/cljs/snapshot/lumo/repl.cljs index 1bc559b3..e75a8e90 100644 --- a/src/cljs/snapshot/lumo/repl.cljs +++ b/src/cljs/snapshot/lumo/repl.cljs @@ -1007,6 +1007,7 @@ lumo.js-deps lumo.repl lumo.repl-resources + lumo.classpath cognitect.transit lazy-map.core cljs.source-map @@ -1091,6 +1092,7 @@ (map str (keys special-doc-map)) (map str (keys repl-special-doc-map))))]))) +;; TODO: "str/" returns all cljs.core symbols (defn ^:export get-completions [line cb] (let [js-matches (re-find #"js/(\S*)$" line)] diff --git a/src/js/__tests__/lumo-test.js b/src/js/__tests__/lumo-test.js index 37ae1721..7ce73851 100644 --- a/src/js/__tests__/lumo-test.js +++ b/src/js/__tests__/lumo-test.js @@ -132,6 +132,7 @@ describe('lumo', () => { describe('readSource', () => { const pathResolve = path.resolve; + beforeEach(() => { jest.resetModules(); lumo = require('../lumo'); // eslint-disable-line global-require @@ -145,7 +146,7 @@ describe('lumo', () => { it('cycles through the source paths', () => { const srcPaths = ['a', 'b', 'c']; lumo.addSourcePaths(srcPaths); - const lumoPaths = ['', ...srcPaths]; + const lumoPaths = [process.cwd(), ...srcPaths]; fs.readFileSync = jest.fn((filename: string) => { throw new Error(`file doesn't exist: ${filename}`); @@ -229,7 +230,7 @@ describe('lumo', () => { it('cycles through the source paths', () => { const srcPaths = ['a', 'b', 'c']; lumo.addSourcePaths(srcPaths); - const lumoPaths = ['', ...srcPaths]; + const lumoPaths = [process.cwd(), ...srcPaths]; fs.existsSync = jest.fn((_: string) => false); diff --git a/src/js/cljs.js b/src/js/cljs.js index 5535ac1f..e813eb91 100644 --- a/src/js/cljs.js +++ b/src/js/cljs.js @@ -152,7 +152,8 @@ function newDevelopmentContext(): vm$Context { readSourceFromJar: lumo.readSourceFromJar, eval: lumoEval, addSourcePaths: lumo.addSourcePaths, - readSourcePaths: lumo.readSourcePaths, + getSourcePaths: lumo.getSourcePaths, + removeSourcePath: lumo.removeSourcePath, }, global: undefined, }; @@ -183,7 +184,8 @@ function newClojureScriptContext(): { [key: string]: mixed } { readSourceFromJar: lumo.readSourceFromJar, eval: lumoEval, addSourcePaths: lumo.addSourcePaths, - readSourcePaths: lumo.readSourcePaths, + getSourcePaths: lumo.getSourcePaths, + removeSourcePath: lumo.removeSourcePath, }; return global; diff --git a/src/js/lumo.js b/src/js/lumo.js index 39820224..cb6a1628 100644 --- a/src/js/lumo.js +++ b/src/js/lumo.js @@ -11,8 +11,7 @@ import JSZip from 'jszip'; import ArrayStream from './array-stream'; import * as util from './util'; -// TODO: make this a set -const sourcePaths = ['']; +const sourcePaths = new Set([process.cwd()]); type SourceType = {| source: string, @@ -77,10 +76,7 @@ export function getGoogleClosureCompiler(): Function { // TODO: cache JARs that we know have a given file / path export function readSource(filename: string): ?SourceType { - const len = sourcePaths.length; - for (let i = 0; i < len; i += 1) { - const srcPath = sourcePaths[i]; - + for (const srcPath of sourcePaths.values()) { try { if (srcPath.endsWith('.jar')) { const data = fs.readFileSync(srcPath); @@ -124,7 +120,8 @@ export function writeCache(filename: string, source: string): ?Error { } export function loadUpstreamForeignLibs(): string[] { - return sourcePaths.reduce((ret: string[], srcPath: string) => { + const ret = []; + for (const srcPath of sourcePaths.values()) { if (srcPath.endsWith('.jar')) { try { const data = fs.readFileSync(srcPath); @@ -136,13 +133,11 @@ export function loadUpstreamForeignLibs(): string[] { } } catch (_) {} // eslint-disable-line no-empty } - return ret; - }, []); + } + return ret; } export function resource(filename: string): ?ResourceType { - const len = sourcePaths.length; - if (isBundled(filename)) { return { type: 'bundled', @@ -150,9 +145,7 @@ export function resource(filename: string): ?ResourceType { }; } - for (let i = 0; i < len; i += 1) { - const srcPath = sourcePaths[i]; - + for (const srcPath of sourcePaths.values()) { if (srcPath.endsWith('.jar')) { const data = fs.readFileSync(srcPath); const zip = new JSZip().load(data); @@ -179,17 +172,20 @@ export function resource(filename: string): ?ResourceType { return null; } +export function getSourcePaths(): string[] { + return [...sourcePaths]; +} + export function addSourcePaths(srcPaths: string[]): void { const expanded = srcPaths.map((srcPath: string) => - // $FlowIssue path.normalize(util.expandPath(srcPath)), ); - sourcePaths.push(...expanded); + expanded.forEach((p: string) => sourcePaths.add(p)); } -export function readSourcePaths(): string[] { - return [...sourcePaths]; +export function removeSourcePath(srcPath: string): boolean { + return sourcePaths.delete(util.expandPath(srcPath)); } export function readSourceFromJar({