pxt-core 13.2.9 → 13.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/cli.js +14 -1
- package/built/commandparser.d.ts +7 -1
- package/built/commandparser.js +7 -1
- package/built/localrepos.d.ts +65 -0
- package/built/localrepos.js +169 -0
- package/built/pxt.js +151 -48
- package/built/pxtlib.d.ts +14 -0
- package/built/pxtlib.js +107 -31
- package/built/pxtsim.d.ts +13 -0
- package/built/pxtsim.js +30 -16
- package/built/server.d.ts +1 -0
- package/built/server.js +14 -0
- package/built/target.js +1 -1
- package/built/targetlight.js +1 -1
- package/built/tests/blocksrunner.js +3 -2
- package/built/tests/blockssetup.js +3 -2
- package/built/tests/cli-test/cli/localrepos.d.ts +65 -0
- package/built/tests/cli-test/tests/cli-test/clirunner.d.ts +2 -0
- package/built/tests/simdriver-test/simdriverrunner.d.ts +2 -0
- package/built/web/main.js +30 -30
- package/built/web/pxtapp.js +2 -2
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtembed.js +1 -1
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtsim.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/runnerembed.js +1 -1
- package/built/web/semantic.css +1 -1
- package/docfiles/evaluate-button.html +1 -0
- package/docfiles/script.html +1 -1
- package/package.json +1 -1
- package/react-common/components/controls/TeachingBubble.tsx +1 -1
- package/theme/common.less +5 -0
package/built/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ const chalk = require('chalk');
|
|
|
18
18
|
var U = pxt.Util;
|
|
19
19
|
var Cloud = pxt.Cloud;
|
|
20
20
|
const server = require("./server");
|
|
21
|
+
const localrepos = require("./localrepos");
|
|
21
22
|
const build = require("./buildengine");
|
|
22
23
|
const commandParser = require("./commandparser");
|
|
23
24
|
const hid = require("./hid");
|
|
@@ -2749,6 +2750,9 @@ function serveAsync(parsed) {
|
|
|
2749
2750
|
saveConfig();
|
|
2750
2751
|
}
|
|
2751
2752
|
let localToken = exports.globalConfig.localToken;
|
|
2753
|
+
// --local-repo directories resolve against the directory serve was started from,
|
|
2754
|
+
// which differs from the target root when serve moves into a target found below
|
|
2755
|
+
const invocationDir = process.cwd();
|
|
2752
2756
|
if (!fs.existsSync("pxtarget.json")) {
|
|
2753
2757
|
//Specifically when the target is being used as a library
|
|
2754
2758
|
let targetDepLoc = nodeutil.targetDir;
|
|
@@ -2767,6 +2771,8 @@ function serveAsync(parsed) {
|
|
|
2767
2771
|
}
|
|
2768
2772
|
}
|
|
2769
2773
|
}
|
|
2774
|
+
const targetDir = process.cwd();
|
|
2775
|
+
const localRepos = localrepos.loadLocalRepos(targetDir, parsed.flags["localRepo"] || [], invocationDir);
|
|
2770
2776
|
return (justServe ? Promise.resolve() : buildAndWatchTargetAsync(includeSourceMaps, rebundle))
|
|
2771
2777
|
.then(() => server.serveAsync({
|
|
2772
2778
|
autoStart: !exports.globalConfig.noAutoStart,
|
|
@@ -2780,6 +2786,7 @@ function serveAsync(parsed) {
|
|
|
2780
2786
|
noauth: parsed.flags["noauth"] || false,
|
|
2781
2787
|
backport: parsed.flags["backport"] || 0,
|
|
2782
2788
|
https: parsed.flags["https"] || false,
|
|
2789
|
+
localRepos,
|
|
2783
2790
|
}));
|
|
2784
2791
|
}
|
|
2785
2792
|
exports.serveAsync = serveAsync;
|
|
@@ -6763,7 +6770,13 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
|
|
|
6763
6770
|
argument: "backport",
|
|
6764
6771
|
type: "number",
|
|
6765
6772
|
},
|
|
6766
|
-
https: { description: "use https protocol instead of http" }
|
|
6773
|
+
https: { description: "use https protocol instead of http" },
|
|
6774
|
+
localRepo: {
|
|
6775
|
+
description: "serve a GitHub repository from a local directory, as owner/repo=dir; repeatable, and applied over the entries of pxtlocal.json",
|
|
6776
|
+
aliases: ["local-repo"],
|
|
6777
|
+
argument: "repo",
|
|
6778
|
+
repeatable: true
|
|
6779
|
+
}
|
|
6767
6780
|
}
|
|
6768
6781
|
}, serveAsync);
|
|
6769
6782
|
p.defineCommand({
|
package/built/commandparser.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface CommandFlag {
|
|
|
7
7
|
possibleValues?: (string | RegExp)[];
|
|
8
8
|
deprecated?: boolean;
|
|
9
9
|
hidden?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The flag may be given more than once, and its values are collected into a
|
|
12
|
+
* string[]. Applies to string flags with an argument only: defineCommand
|
|
13
|
+
* throws on a repeatable flag of another type or without an argument.
|
|
14
|
+
*/
|
|
15
|
+
repeatable?: boolean;
|
|
10
16
|
}
|
|
11
17
|
export interface Command {
|
|
12
18
|
name: string;
|
|
@@ -30,7 +36,7 @@ export interface ParsedCommand {
|
|
|
30
36
|
name: string;
|
|
31
37
|
args: string[];
|
|
32
38
|
flags: {
|
|
33
|
-
[index: string]: boolean | string | number;
|
|
39
|
+
[index: string]: boolean | string | number | string[];
|
|
34
40
|
};
|
|
35
41
|
}
|
|
36
42
|
export declare class CommandParser {
|
package/built/commandparser.js
CHANGED
|
@@ -13,6 +13,9 @@ class CommandParser {
|
|
|
13
13
|
const aliasMap = {};
|
|
14
14
|
for (const flag in c.flags) {
|
|
15
15
|
const def = c.flags[flag];
|
|
16
|
+
if (def.repeatable && ((def.type && def.type !== "string") || !def.argument)) {
|
|
17
|
+
throw new Error(`Flag ${flag} of command ${c.name} is repeatable, so it must be a string flag with an argument`);
|
|
18
|
+
}
|
|
16
19
|
recordAlias(flag, flag);
|
|
17
20
|
const aliases = c.flags[flag].aliases;
|
|
18
21
|
if (aliases) {
|
|
@@ -83,7 +86,10 @@ class CommandParser {
|
|
|
83
86
|
if (currentFlagDef.possibleValues && currentFlagDef.possibleValues.length && !nodeutil.matchesAny(match[2], currentFlagDef.possibleValues)) {
|
|
84
87
|
pxt.U.userError(`Unknown value for flag '${currentFlag}', '${match[2]}'`);
|
|
85
88
|
}
|
|
86
|
-
if (
|
|
89
|
+
if (currentFlagDef.repeatable) {
|
|
90
|
+
flags[currentFlag] = (flags[currentFlag] || []).concat([match[2]]);
|
|
91
|
+
}
|
|
92
|
+
else if (!currentFlagDef.type || currentFlagDef.type === "string") {
|
|
87
93
|
flags[currentFlag] = match[2];
|
|
88
94
|
}
|
|
89
95
|
else if (currentFlagDef.type === "boolean") {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Name of the machine-local file, at the root of a target checkout, that maps
|
|
3
|
+
* GitHub repositories to local directories for `pxt serve`. It holds paths of
|
|
4
|
+
* the developer's machine, so it is meant to stay out of source control.
|
|
5
|
+
*/
|
|
6
|
+
export declare const LOCAL_CONFIG_NAME = "pxtlocal.json";
|
|
7
|
+
/**
|
|
8
|
+
* Contents of pxtlocal.json.
|
|
9
|
+
*/
|
|
10
|
+
export interface LocalConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Maps a GitHub repository ("owner/repo") to the local directory holding
|
|
13
|
+
* its checkout, absolute or relative to the directory of pxtlocal.json.
|
|
14
|
+
*/
|
|
15
|
+
localRepos?: pxt.Map<string>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Repository metadata, in the shape the MakeCode GitHub proxy returns it.
|
|
19
|
+
*/
|
|
20
|
+
export interface LocalRepoMeta {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Refs listing, in the shape the MakeCode GitHub proxy returns it.
|
|
26
|
+
*/
|
|
27
|
+
export interface LocalRepoRefs {
|
|
28
|
+
refs: pxt.Map<string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Builds the map of GitHub repositories that `pxt serve` resolves from local
|
|
32
|
+
* directories.
|
|
33
|
+
*
|
|
34
|
+
* Entries come from pxtlocal.json in `targetDir`, when the file exists, with
|
|
35
|
+
* relative directories resolved against `targetDir`. Each `flagValue`
|
|
36
|
+
* ("owner/repo=dir", relative directories resolved against `cwd`) is applied
|
|
37
|
+
* after the file and replaces a file entry for the same repository.
|
|
38
|
+
*
|
|
39
|
+
* Returns the map from lowercase "owner/repo" to absolute directory. Throws a
|
|
40
|
+
* user error when pxtlocal.json is not valid JSON, or an entry names
|
|
41
|
+
* something other than a repository or a directory that does not exist.
|
|
42
|
+
*/
|
|
43
|
+
export declare function loadLocalRepos(targetDir: string, flagValues: string[], cwd: string): pxt.Map<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Answers a request for an aliased repository in the shape the MakeCode
|
|
46
|
+
* GitHub proxy answers it, reading the repository's local directory. The
|
|
47
|
+
* request is the path after "gh/":
|
|
48
|
+
*
|
|
49
|
+
* - "owner/repo": the repository metadata ({@link LocalRepoMeta}); the
|
|
50
|
+
* description is the one in the repository's root pxt.json, if any
|
|
51
|
+
* - "owner/repo/refs": the refs listing ({@link LocalRepoRefs}), always
|
|
52
|
+
* empty, because an aliased repository serves its working tree whatever
|
|
53
|
+
* ref is asked for
|
|
54
|
+
* - "owner/repo[/path]/text": the files of the package at `path` (the
|
|
55
|
+
* repository root when omitted), keyed by file name: its pxt.json and
|
|
56
|
+
* every file that pxt.json lists
|
|
57
|
+
*
|
|
58
|
+
* Throws an error carrying the HTTP `statusCode` to answer with: 404 when
|
|
59
|
+
* the repository is not aliased, or when the package's pxt.json or a file it
|
|
60
|
+
* lists does not exist; 400 when the path lies outside the repository's
|
|
61
|
+
* directory, the pxt.json is not valid or lists a file outside the package's
|
|
62
|
+
* directory, or the request matches none of the forms above. Every error is
|
|
63
|
+
* also written to the console.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveLocalRepoRequest(localRepos: pxt.Map<string>, request: string): LocalRepoMeta | LocalRepoRefs | pxt.Map<string>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveLocalRepoRequest = exports.loadLocalRepos = exports.LOCAL_CONFIG_NAME = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
/**
|
|
7
|
+
* Name of the machine-local file, at the root of a target checkout, that maps
|
|
8
|
+
* GitHub repositories to local directories for `pxt serve`. It holds paths of
|
|
9
|
+
* the developer's machine, so it is meant to stay out of source control.
|
|
10
|
+
*/
|
|
11
|
+
exports.LOCAL_CONFIG_NAME = "pxtlocal.json";
|
|
12
|
+
function requestError(statusCode, msg) {
|
|
13
|
+
const err = new Error(msg);
|
|
14
|
+
err.statusCode = statusCode;
|
|
15
|
+
return err;
|
|
16
|
+
}
|
|
17
|
+
function isDirectory(dir) {
|
|
18
|
+
try {
|
|
19
|
+
return fs.statSync(dir).isDirectory();
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function readTextOpt(fn) {
|
|
26
|
+
try {
|
|
27
|
+
return fs.readFileSync(fn, "utf8");
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (e.code == "ENOENT" || e.code == "ENOTDIR")
|
|
31
|
+
return undefined;
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves `rel` against the absolute directory `base`, and returns the
|
|
37
|
+
* resolved path if it is `base` or lies inside it, undefined otherwise.
|
|
38
|
+
*/
|
|
39
|
+
function resolveInside(base, rel) {
|
|
40
|
+
const resolved = path.resolve(base, rel);
|
|
41
|
+
const prefix = base.endsWith(path.sep) ? base : base + path.sep;
|
|
42
|
+
return resolved == base || resolved.startsWith(prefix) ? resolved : undefined;
|
|
43
|
+
}
|
|
44
|
+
function addLocalRepo(localRepos, slug, dir, baseDir, source) {
|
|
45
|
+
const repo = pxt.github.parseRepoId(slug);
|
|
46
|
+
if (!repo || repo.fileName || repo.tag !== undefined || repo.slug != slug.trim())
|
|
47
|
+
pxt.U.userError(`${source}: '${slug}' is not a GitHub repository; expected owner/repo`);
|
|
48
|
+
if (!dir)
|
|
49
|
+
pxt.U.userError(`${source}: no directory given for ${slug}`);
|
|
50
|
+
const resolved = path.resolve(baseDir, dir);
|
|
51
|
+
if (!isDirectory(resolved))
|
|
52
|
+
pxt.U.userError(`${source}: directory ${resolved} for ${slug} does not exist`);
|
|
53
|
+
localRepos[repo.slug.toLowerCase()] = resolved;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Builds the map of GitHub repositories that `pxt serve` resolves from local
|
|
57
|
+
* directories.
|
|
58
|
+
*
|
|
59
|
+
* Entries come from pxtlocal.json in `targetDir`, when the file exists, with
|
|
60
|
+
* relative directories resolved against `targetDir`. Each `flagValue`
|
|
61
|
+
* ("owner/repo=dir", relative directories resolved against `cwd`) is applied
|
|
62
|
+
* after the file and replaces a file entry for the same repository.
|
|
63
|
+
*
|
|
64
|
+
* Returns the map from lowercase "owner/repo" to absolute directory. Throws a
|
|
65
|
+
* user error when pxtlocal.json is not valid JSON, or an entry names
|
|
66
|
+
* something other than a repository or a directory that does not exist.
|
|
67
|
+
*/
|
|
68
|
+
function loadLocalRepos(targetDir, flagValues, cwd) {
|
|
69
|
+
const localRepos = {};
|
|
70
|
+
const configPath = path.join(targetDir, exports.LOCAL_CONFIG_NAME);
|
|
71
|
+
const configText = readTextOpt(configPath);
|
|
72
|
+
if (configText !== undefined) {
|
|
73
|
+
const config = pxt.U.jsonTryParse(configText);
|
|
74
|
+
if (!config)
|
|
75
|
+
pxt.U.userError(`${configPath}: invalid JSON`);
|
|
76
|
+
const entries = config.localRepos || {};
|
|
77
|
+
for (const slug of Object.keys(entries))
|
|
78
|
+
addLocalRepo(localRepos, slug, entries[slug], targetDir, configPath);
|
|
79
|
+
}
|
|
80
|
+
for (const flagValue of flagValues) {
|
|
81
|
+
const m = /^([^=]+)=(.*)$/.exec(flagValue);
|
|
82
|
+
if (!m)
|
|
83
|
+
pxt.U.userError(`--local-repo ${flagValue}: expected owner/repo=directory`);
|
|
84
|
+
addLocalRepo(localRepos, m[1], m[2], cwd, `--local-repo ${flagValue}`);
|
|
85
|
+
}
|
|
86
|
+
return localRepos;
|
|
87
|
+
}
|
|
88
|
+
exports.loadLocalRepos = loadLocalRepos;
|
|
89
|
+
function readPackageFiles(fullName, root, fileName) {
|
|
90
|
+
const dir = resolveInside(root, fileName);
|
|
91
|
+
if (!dir)
|
|
92
|
+
throw requestError(400, `${fullName}: the path is outside the repository`);
|
|
93
|
+
pxt.debug(`local repo: ${fullName} from ${dir}`);
|
|
94
|
+
const configPath = path.join(dir, pxt.CONFIG_NAME);
|
|
95
|
+
const configText = readTextOpt(configPath);
|
|
96
|
+
if (configText === undefined)
|
|
97
|
+
throw requestError(404, `${fullName}: ${configPath} does not exist (the repository is served from ${root})`);
|
|
98
|
+
const config = pxt.Package.parseAndValidConfig(configText);
|
|
99
|
+
if (!config)
|
|
100
|
+
throw requestError(400, `${fullName}: ${configPath} is not a valid ${pxt.CONFIG_NAME}`);
|
|
101
|
+
const files = {};
|
|
102
|
+
files[pxt.CONFIG_NAME] = configText;
|
|
103
|
+
for (const fn of pxt.allPkgFiles(config).slice(1)) {
|
|
104
|
+
const filePath = resolveInside(dir, fn);
|
|
105
|
+
if (!filePath)
|
|
106
|
+
throw requestError(400, `${fullName}: ${configPath} lists ${fn}, which is outside the package directory`);
|
|
107
|
+
const text = readTextOpt(filePath);
|
|
108
|
+
if (text === undefined)
|
|
109
|
+
throw requestError(404, `${fullName}: ${filePath}, listed in ${configPath}, does not exist`);
|
|
110
|
+
files[fn] = text;
|
|
111
|
+
}
|
|
112
|
+
return files;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Answers a request for an aliased repository in the shape the MakeCode
|
|
116
|
+
* GitHub proxy answers it, reading the repository's local directory. The
|
|
117
|
+
* request is the path after "gh/":
|
|
118
|
+
*
|
|
119
|
+
* - "owner/repo": the repository metadata ({@link LocalRepoMeta}); the
|
|
120
|
+
* description is the one in the repository's root pxt.json, if any
|
|
121
|
+
* - "owner/repo/refs": the refs listing ({@link LocalRepoRefs}), always
|
|
122
|
+
* empty, because an aliased repository serves its working tree whatever
|
|
123
|
+
* ref is asked for
|
|
124
|
+
* - "owner/repo[/path]/text": the files of the package at `path` (the
|
|
125
|
+
* repository root when omitted), keyed by file name: its pxt.json and
|
|
126
|
+
* every file that pxt.json lists
|
|
127
|
+
*
|
|
128
|
+
* Throws an error carrying the HTTP `statusCode` to answer with: 404 when
|
|
129
|
+
* the repository is not aliased, or when the package's pxt.json or a file it
|
|
130
|
+
* lists does not exist; 400 when the path lies outside the repository's
|
|
131
|
+
* directory, the pxt.json is not valid or lists a file outside the package's
|
|
132
|
+
* directory, or the request matches none of the forms above. Every error is
|
|
133
|
+
* also written to the console.
|
|
134
|
+
*/
|
|
135
|
+
function resolveLocalRepoRequest(localRepos, request) {
|
|
136
|
+
try {
|
|
137
|
+
return answerLocalRepoRequest(localRepos, request);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
pxt.error(e.message);
|
|
141
|
+
throw e;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.resolveLocalRepoRequest = resolveLocalRepoRequest;
|
|
145
|
+
function answerLocalRepoRequest(localRepos, request) {
|
|
146
|
+
const elts = request.split("/").filter(e => !!e);
|
|
147
|
+
const repo = elts.length >= 2 ? pxt.github.parseRepoId(elts.slice(0, 2).join("/")) : undefined;
|
|
148
|
+
const root = repo && localRepos[repo.slug.toLowerCase()];
|
|
149
|
+
if (!root)
|
|
150
|
+
throw requestError(404, `${request}: not a locally served repository`);
|
|
151
|
+
const rest = elts.slice(2);
|
|
152
|
+
if (!rest.length) {
|
|
153
|
+
pxt.debug(`local repo: ${repo.slug} metadata from ${root}`);
|
|
154
|
+
const rootConfig = pxt.Package.parseAndValidConfig(readTextOpt(path.join(root, pxt.CONFIG_NAME)));
|
|
155
|
+
return {
|
|
156
|
+
name: repo.project,
|
|
157
|
+
description: (rootConfig === null || rootConfig === void 0 ? void 0 : rootConfig.description) || ""
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (rest.length == 1 && rest[0] == "refs") {
|
|
161
|
+
pxt.debug(`local repo: ${repo.slug} refs (working tree only)`);
|
|
162
|
+
return { refs: {} };
|
|
163
|
+
}
|
|
164
|
+
if (rest[rest.length - 1] == "text") {
|
|
165
|
+
const fileName = rest.slice(0, -1).join("/");
|
|
166
|
+
return readPackageFiles(pxt.github.join(repo.slug, fileName), root, fileName);
|
|
167
|
+
}
|
|
168
|
+
throw requestError(400, `${request}: unknown local repository request`);
|
|
169
|
+
}
|