@m2c2kit/cli 0.1.6 → 0.1.10
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/dist/.env
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
CLI_VERSION=0.1.
|
|
1
|
+
CLI_VERSION=0.1.10
|
package/dist/cli.js
CHANGED
|
@@ -146,6 +146,10 @@ await yarg
|
|
|
146
146
|
sourceFilePath: path.join(packageHomeFolderPath, "fonts", "roboto", "Roboto-Regular.ttf"),
|
|
147
147
|
destinationFilePath: path.join(newFolderPath, "fonts", "roboto", "Roboto-Regular.ttf"),
|
|
148
148
|
},
|
|
149
|
+
{
|
|
150
|
+
sourceFilePath: path.join(packageHomeFolderPath, "scripts", "post-install.mjs"),
|
|
151
|
+
destinationFilePath: path.join(newFolderPath, "post-install.mjs"),
|
|
152
|
+
},
|
|
149
153
|
];
|
|
150
154
|
let errorCopyingFiles = false;
|
|
151
155
|
copyFiles.forEach((c) => {
|
|
@@ -266,25 +270,10 @@ await yarg
|
|
|
266
270
|
if (abort) {
|
|
267
271
|
yarg.exit(1, new Error("user aborted"));
|
|
268
272
|
}
|
|
269
|
-
// get study code first from command line, then from config file, then
|
|
270
|
-
|
|
273
|
+
// get study code first from command line, then from config file, then generate new one
|
|
274
|
+
const studyCode = argv["studyCode"] ??
|
|
271
275
|
serverConfig?.studyCode ??
|
|
272
|
-
|
|
273
|
-
if (studyCode === "") {
|
|
274
|
-
const response = await prompts({
|
|
275
|
-
type: "text",
|
|
276
|
-
name: "studyCode",
|
|
277
|
-
message: "study code?",
|
|
278
|
-
validate: (text) => text.length > 0
|
|
279
|
-
? true
|
|
280
|
-
: "provide a study code (5 random alphanumeric digits, e.g., R5T7A)",
|
|
281
|
-
onState: (state) => (abort = state.aborted === true),
|
|
282
|
-
});
|
|
283
|
-
studyCode = response.studyCode;
|
|
284
|
-
}
|
|
285
|
-
if (abort) {
|
|
286
|
-
yarg.exit(1, new Error("user aborted"));
|
|
287
|
-
}
|
|
276
|
+
generateStudyCode();
|
|
288
277
|
// if new url or study code was provided, save these
|
|
289
278
|
if (url !== serverConfig?.url || studyCode !== serverConfig?.studyCode) {
|
|
290
279
|
m2c2kitProjectConfig.set("server", { url, studyCode });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { resolve, basename } from "path";
|
|
4
|
+
|
|
5
|
+
const { dependencies } = JSON.parse(fs.readFileSync("./package.json"));
|
|
6
|
+
|
|
7
|
+
/** If this m2c2kit app uses surveys, then copy the required css
|
|
8
|
+
* files from node_modules so they can be bundled */
|
|
9
|
+
if (Object.keys(dependencies).includes("@m2c2kit/survey")) {
|
|
10
|
+
// does ./css folder exist? if not, create
|
|
11
|
+
if (!fs.existsSync("./css")) {
|
|
12
|
+
fs.mkdirSync("./css");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const cssDistDir = "./node_modules/@m2c2kit/survey/dist/css/";
|
|
16
|
+
const cssFiles = fs
|
|
17
|
+
.readdirSync(cssDistDir, {
|
|
18
|
+
withFileTypes: true,
|
|
19
|
+
})
|
|
20
|
+
.filter((dirent) => !dirent.isDirectory())
|
|
21
|
+
.map((dirent) => resolve(cssDistDir, dirent.name));
|
|
22
|
+
|
|
23
|
+
cssFiles.forEach((file) => {
|
|
24
|
+
const sourceContents = fs.readFileSync(file);
|
|
25
|
+
fs.writeFileSync(`./css/${basename(file)}`, sourceContents);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"serve": "rollup -c --watch --configServe",
|
|
6
|
-
"build": "rollup -c --configProd"
|
|
6
|
+
"build": "rollup -c --configProd",
|
|
7
|
+
"postinstall": "node ./post-install.mjs"
|
|
7
8
|
},
|
|
8
9
|
"private": true,
|
|
9
10
|
"dependencies": {
|
|
10
|
-
"@m2c2kit/core": "0.1.
|
|
11
|
-
"@m2c2kit/addons": "0.1.
|
|
11
|
+
"@m2c2kit/core": "0.1.8",
|
|
12
|
+
"@m2c2kit/addons": "0.1.8"
|
|
12
13
|
},
|
|
13
14
|
"devDependencies": {
|
|
14
15
|
"rollup": "2.63.0",
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
-
WebColors,
|
|
3
|
-
Action,
|
|
4
2
|
Game,
|
|
3
|
+
Action,
|
|
5
4
|
Scene,
|
|
6
|
-
|
|
5
|
+
Shape,
|
|
7
6
|
Point,
|
|
8
7
|
Label,
|
|
9
|
-
|
|
10
|
-
Shape,
|
|
8
|
+
WebColors,
|
|
11
9
|
Rect,
|
|
12
|
-
|
|
10
|
+
LabelHorizontalAlignmentMode,
|
|
13
11
|
GameParameters,
|
|
12
|
+
GameOptions,
|
|
14
13
|
TrialSchema,
|
|
15
14
|
Session,
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
SessionLifecycleEvent,
|
|
16
|
+
ActivityDataEvent,
|
|
17
|
+
ActivityLifecycleEvent,
|
|
18
|
+
Sprite,
|
|
18
19
|
} from "@m2c2kit/core";
|
|
19
20
|
import { Button, Instructions } from "@m2c2kit/addons";
|
|
20
21
|
|
|
21
22
|
class {{className}} extends Game {
|
|
22
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
-
constructor(
|
|
24
|
+
constructor() {
|
|
25
|
+
/**
|
|
26
|
+
* These are configurable game parameters and their defaults.
|
|
27
|
+
* At run time, they can be changed with the setParameters() method.
|
|
28
|
+
*/
|
|
24
29
|
const defaultParameters: GameParameters = {
|
|
25
30
|
ReadyTime: {
|
|
26
31
|
value: 1000,
|
|
@@ -30,6 +35,12 @@ class {{className}} extends Game {
|
|
|
30
35
|
TrialNum: { value: 3, description: "How many trials to run" },
|
|
31
36
|
};
|
|
32
37
|
|
|
38
|
+
/**
|
|
39
|
+
* This describes all the data that will be generated by the assessment.
|
|
40
|
+
* At runtime, when a trial completes, the data will be returned to the
|
|
41
|
+
* session with a callback, along with this schema transformed into
|
|
42
|
+
* JSON Schema Draft-07 format.
|
|
43
|
+
*/
|
|
33
44
|
const demoTrialSchema: TrialSchema = {
|
|
34
45
|
colorChosen: { type: "string", description: "the color that was picked" },
|
|
35
46
|
correct: { type: "boolean", description: "was the answer correct?" },
|
|
@@ -44,9 +55,9 @@ class {{className}} extends Game {
|
|
|
44
55
|
showFps: true,
|
|
45
56
|
trialSchema: demoTrialSchema,
|
|
46
57
|
parameters: defaultParameters,
|
|
47
|
-
// set this color so we can see the boundaries of the game during development,
|
|
48
|
-
// but typically we would not set this
|
|
49
|
-
bodyBackgroundColor: WebColors.Wheat,
|
|
58
|
+
// You can set this color so we can see the boundaries of the game during development,
|
|
59
|
+
// but typically we would not set this, so it is commented out
|
|
60
|
+
// bodyBackgroundColor: WebColors.Wheat,
|
|
50
61
|
// note: using 2:1 aspect ratio, because that is closer to modern phones
|
|
51
62
|
width: 400,
|
|
52
63
|
height: 800,
|
|
@@ -76,7 +87,7 @@ class {{className}} extends Game {
|
|
|
76
87
|
],
|
|
77
88
|
};
|
|
78
89
|
|
|
79
|
-
super(options
|
|
90
|
+
super(options);
|
|
80
91
|
// just for convenience, alias the variable game to "this"
|
|
81
92
|
// (even though eslint doesn't like it)
|
|
82
93
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -94,6 +105,7 @@ class {{className}} extends Game {
|
|
|
94
105
|
textFontSize: 24,
|
|
95
106
|
titleFontSize: 30,
|
|
96
107
|
image: "smiley",
|
|
108
|
+
imageMarginBottom: 24
|
|
97
109
|
},
|
|
98
110
|
{
|
|
99
111
|
title: "{{appName}} Demo",
|
|
@@ -298,11 +310,6 @@ class {{className}} extends Game {
|
|
|
298
310
|
});
|
|
299
311
|
startOverButton.isUserInteractionEnabled = true;
|
|
300
312
|
startOverButton.onTapDown(() => {
|
|
301
|
-
// in the setup() for the end scene, we animate the smiley sprite with
|
|
302
|
-
// a move action. if the user taps the start over button before the
|
|
303
|
-
// animation is completed, we should remove it by calling
|
|
304
|
-
// removeAllActions()
|
|
305
|
-
smileySprite.removeAllActions();
|
|
306
313
|
game.initData();
|
|
307
314
|
game.presentScene(getReadyScene);
|
|
308
315
|
});
|
|
@@ -322,56 +329,86 @@ class {{className}} extends Game {
|
|
|
322
329
|
});
|
|
323
330
|
endScene.addChild(exitButton);
|
|
324
331
|
|
|
325
|
-
const smileySprite = new Sprite({ imageName: "smiley" });
|
|
326
|
-
endScene.addChild(smileySprite);
|
|
327
|
-
|
|
328
332
|
endScene.setup(() => {
|
|
329
333
|
doneLabel.text = `You did ${game.trialIndex} trials. You're done!`;
|
|
330
|
-
|
|
331
|
-
// example of how to position a sprite and create an action to move it
|
|
332
|
-
smileySprite.position = new Point(200, 500);
|
|
333
|
-
smileySprite.run(
|
|
334
|
-
Action.Move({ point: new Point(200, 100), duration: 3000 })
|
|
335
|
-
);
|
|
336
334
|
});
|
|
337
335
|
|
|
338
336
|
game.entryScene = "instructions-01";
|
|
339
337
|
}
|
|
340
338
|
}
|
|
341
339
|
|
|
340
|
+
const game1 = new {{className}}();
|
|
342
341
|
// default was 3 trials; this is how we can specify a different value
|
|
343
|
-
|
|
342
|
+
game1.setParameters({ TrialNum: 2 });
|
|
344
343
|
|
|
345
344
|
const session = new Session({
|
|
346
345
|
activities: [game1],
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
346
|
+
sessionCallbacks: {
|
|
347
|
+
/**
|
|
348
|
+
* onSessionLifecycleChange() will be called on events such
|
|
349
|
+
* as when the session initialization is complete or when it
|
|
350
|
+
* ends.
|
|
351
|
+
*
|
|
352
|
+
* Once initialized, the session will automatically start,
|
|
353
|
+
* unless we're running in an Android WebView AND a manual start
|
|
354
|
+
* is desired.
|
|
355
|
+
*/
|
|
356
|
+
onSessionLifecycleChange: (ev: SessionLifecycleEvent) => {
|
|
357
|
+
if (ev.initialized) {
|
|
358
|
+
session.start();
|
|
359
|
+
}
|
|
360
|
+
if (ev.ended) {
|
|
361
|
+
console.log("session ended");
|
|
362
|
+
}
|
|
355
363
|
},
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
364
|
+
},
|
|
365
|
+
activityCallbacks: {
|
|
366
|
+
/**
|
|
367
|
+
* onActivityDataCreate() is where you insert code to post data to an API
|
|
368
|
+
* or interop with a native function in the host app.
|
|
369
|
+
*
|
|
370
|
+
* newData is the data that was just generated by the completed trial
|
|
371
|
+
* data is all the data, cumulative of all trials, that have been generated.
|
|
372
|
+
*
|
|
373
|
+
* We separate out newData from data in case you want to alter the execution
|
|
374
|
+
* based on the most recent trial, e.g., maybe you want to stop after
|
|
375
|
+
* a certain user behavior or performance threshold in the just completed
|
|
376
|
+
* trial.
|
|
377
|
+
*/
|
|
378
|
+
onActivityDataCreate: (ev: ActivityDataEvent) => {
|
|
379
|
+
console.log(`********** trial complete`);
|
|
380
|
+
console.log("newData: " + JSON.stringify(ev.newData));
|
|
381
|
+
console.log("newData schema: " + JSON.stringify(ev.newDataSchema));
|
|
382
|
+
console.log("data: " + JSON.stringify(ev.data));
|
|
383
|
+
console.log("data schema: " + JSON.stringify(ev.dataSchema));
|
|
384
|
+
console.log(
|
|
385
|
+
"activity parameters: " + JSON.stringify(ev.activityConfiguration)
|
|
386
|
+
);
|
|
387
|
+
},
|
|
388
|
+
/**
|
|
389
|
+
* onActivityLifecycleChange() notifies us when an activity, such
|
|
390
|
+
* as an assessment or a survey, has completed. Usually, however,
|
|
391
|
+
* we want to know when all the activities are done, so we'll
|
|
392
|
+
* look for the session ending via onSessionLifecycleChange
|
|
393
|
+
*/
|
|
394
|
+
onActivityLifecycleChange: (ev: ActivityLifecycleEvent) => {
|
|
395
|
+
if (ev.ended) {
|
|
396
|
+
console.log(`ended activity ${ev.name}`);
|
|
362
397
|
if (session.nextActivity) {
|
|
363
398
|
session.advanceToNextActivity();
|
|
399
|
+
} else {
|
|
400
|
+
session.end();
|
|
364
401
|
}
|
|
365
402
|
}
|
|
366
403
|
},
|
|
367
404
|
},
|
|
368
405
|
});
|
|
369
406
|
|
|
370
|
-
|
|
371
|
-
|
|
407
|
+
/**
|
|
408
|
+
* Make session also available on window in case we want to control
|
|
409
|
+
* the session through another means, such as other javascript or
|
|
410
|
+
* browser code, or the Android WebView loadUrl() method
|
|
411
|
+
* */
|
|
372
412
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
373
413
|
(window as unknown as any).session = session;
|
|
374
|
-
|
|
375
|
-
session.init().then(() => {
|
|
376
|
-
session.start();
|
|
377
|
-
});
|
|
414
|
+
session.init();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m2c2kit/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "m2c2kit command line interface",
|
|
5
5
|
"module": "dist/cli.js",
|
|
6
6
|
"files": [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"build": "npm run clean && npm run compile && npm run copy-files && npm run write-dotenv",
|
|
18
18
|
"compile": "tsc",
|
|
19
19
|
"clean": "rimraf dist/ && rimraf build/",
|
|
20
|
-
"copy-files": "copyfiles -f build/src/cli.js dist && copyfiles templates/* fonts/**/* dist/",
|
|
20
|
+
"copy-files": "copyfiles -f build/src/cli.js dist && copyfiles scripts/* templates/* fonts/**/* dist/",
|
|
21
21
|
"write-dotenv": "node write-dotenv.js"
|
|
22
22
|
},
|
|
23
23
|
"author": "",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"form-data": "4.0.0",
|
|
30
30
|
"handlebars": "4.7.7",
|
|
31
31
|
"ora": "6.0.1",
|
|
32
|
-
"prompts": "2.4.2"
|
|
32
|
+
"prompts": "2.4.2",
|
|
33
|
+
"yargs": "17.3.1"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@types/node": "17.0.8",
|
|
@@ -38,7 +39,6 @@
|
|
|
38
39
|
"copyfiles": "2.4.1",
|
|
39
40
|
"rimraf": "3.0.2",
|
|
40
41
|
"tslib": "2.3.1",
|
|
41
|
-
"typescript": "4.5.4"
|
|
42
|
-
"yargs": "17.3.1"
|
|
42
|
+
"typescript": "4.5.4"
|
|
43
43
|
}
|
|
44
44
|
}
|