@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.6
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 assign blank
270
- let studyCode = argv["studyCode"] ??
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.5",
11
- "@m2c2kit/addons": "0.1.5"
11
+ "@m2c2kit/core": "0.1.8",
12
+ "@m2c2kit/addons": "0.1.8"
12
13
  },
13
14
  "devDependencies": {
14
15
  "rollup": "2.63.0",
@@ -58,7 +58,11 @@ export default (commandLineArgs) => {
58
58
  {
59
59
  src: "img/*",
60
60
  dest: `${outputFolder}/img`,
61
- },
61
+ },
62
+ {
63
+ src: "css/*",
64
+ dest: `${outputFolder}/css`,
65
+ },
62
66
  ],
63
67
  copyOnce: false,
64
68
  hook: "writeBundle",
@@ -1,26 +1,31 @@
1
1
  import {
2
- WebColors,
3
- Action,
4
2
  Game,
3
+ Action,
5
4
  Scene,
6
- Sprite,
5
+ Shape,
7
6
  Point,
8
7
  Label,
9
- LabelHorizontalAlignmentMode,
10
- Shape,
8
+ WebColors,
11
9
  Rect,
12
- GameOptions,
10
+ LabelHorizontalAlignmentMode,
13
11
  GameParameters,
12
+ GameOptions,
14
13
  TrialSchema,
15
14
  Session,
16
- GameTrialEvent,
17
- GameLifecycleEvent,
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(specifiedParameters?: any) {
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, specifiedParameters);
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
- const game1 = new {{className}}({ TrialNum: 2 });
342
+ game1.setParameters({ TrialNum: 2 });
344
343
 
345
344
  const session = new Session({
346
345
  activities: [game1],
347
- gameCallbacks: {
348
- // onGameTrialComplete() is where you insert code to post data to an API
349
- // or interop with a native function in the host app, if applicable
350
- onGameTrialComplete: (e: GameTrialEvent) => {
351
- console.log(`********** trial ${e.trialIndex} complete`);
352
- console.log("data: " + JSON.stringify(e.gameData));
353
- console.log("trial schema: " + JSON.stringify(e.trialSchema));
354
- console.log("game parameters: " + JSON.stringify(e.gameParameters));
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
- // onGameEnd() is called when the user is done
357
- onGameEnd: (e: GameLifecycleEvent) => {
358
- if (e.ended) {
359
- console.log(`user requested exit in game ${e.gameName}`);
360
- // this session has only one activity, but this is how it would go to
361
- // the next activity
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
- // make session also available on window in case we want to control
371
- // the session through another means
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.6",
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
  }