feat: add script to annotate challenges for translation (#40366)
* feat: add script to annotate challenges for translation * fix: readdirp-walk -> readdirp * fix: remove notranslate for frontmatter * fix: don't output seed/solution * feat: convert to function Also puts in some missing 'async's to make it clearer what returns promises. * refactor: use meaningful names * refactor: remove comments * chore: update dependencies * chore: move dir * fix(crowdin): annotate individual answers
This commit is contained in:
parent
994598900a
commit
eb8359c281
@ -10,6 +10,7 @@
|
|||||||
"tools/scripts/build",
|
"tools/scripts/build",
|
||||||
"tools/scripts/formatter",
|
"tools/scripts/formatter",
|
||||||
"tools/scripts/formatter/fcc-md-to-gfm",
|
"tools/scripts/formatter/fcc-md-to-gfm",
|
||||||
|
"tools/formatter",
|
||||||
"tools/challenge-helper-scripts"
|
"tools/challenge-helper-scripts"
|
||||||
],
|
],
|
||||||
"version": "independent"
|
"version": "independent"
|
||||||
|
393
tools/formatter/package-lock.json
generated
Normal file
393
tools/formatter/package-lock.json
generated
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
{
|
||||||
|
"name": "fcc-formatter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ=="
|
||||||
|
},
|
||||||
|
"argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"requires": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bail": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ=="
|
||||||
|
},
|
||||||
|
"ccount": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw=="
|
||||||
|
},
|
||||||
|
"character-entities": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="
|
||||||
|
},
|
||||||
|
"character-entities-html4": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g=="
|
||||||
|
},
|
||||||
|
"character-entities-legacy": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="
|
||||||
|
},
|
||||||
|
"character-reference-invalid": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="
|
||||||
|
},
|
||||||
|
"collapse-white-space": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ=="
|
||||||
|
},
|
||||||
|
"esprima": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||||
|
},
|
||||||
|
"extend": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||||
|
},
|
||||||
|
"fault": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
|
||||||
|
"requires": {
|
||||||
|
"format": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
|
||||||
|
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs="
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"is-alphabetical": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="
|
||||||
|
},
|
||||||
|
"is-alphanumeric": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ="
|
||||||
|
},
|
||||||
|
"is-alphanumerical": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
|
||||||
|
"requires": {
|
||||||
|
"is-alphabetical": "^1.0.0",
|
||||||
|
"is-decimal": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-buffer": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
|
||||||
|
},
|
||||||
|
"is-decimal": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="
|
||||||
|
},
|
||||||
|
"is-hexadecimal": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
|
||||||
|
},
|
||||||
|
"is-plain-obj": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="
|
||||||
|
},
|
||||||
|
"is-whitespace-character": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w=="
|
||||||
|
},
|
||||||
|
"is-word-character": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA=="
|
||||||
|
},
|
||||||
|
"js-yaml": {
|
||||||
|
"version": "3.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
|
||||||
|
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
|
||||||
|
"requires": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||||
|
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||||
|
},
|
||||||
|
"longest-streak": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg=="
|
||||||
|
},
|
||||||
|
"markdown-escapes": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg=="
|
||||||
|
},
|
||||||
|
"markdown-table": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==",
|
||||||
|
"requires": {
|
||||||
|
"repeat-string": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mdast-builder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdast-builder/-/mdast-builder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-a3KBk/LmYD6wKsWi8WJrGU/rXR4yuF4Men0JO0z6dSZCm5FrXXWTRDjqK0vGSqa+1M6p9edeuypZAZAzSehTUw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/unist": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mdast-util-compact": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==",
|
||||||
|
"requires": {
|
||||||
|
"unist-util-visit": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse-entities": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
|
||||||
|
"requires": {
|
||||||
|
"character-entities": "^1.0.0",
|
||||||
|
"character-entities-legacy": "^1.0.0",
|
||||||
|
"character-reference-invalid": "^1.0.0",
|
||||||
|
"is-alphanumerical": "^1.0.0",
|
||||||
|
"is-decimal": "^1.0.0",
|
||||||
|
"is-hexadecimal": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remark-frontmatter": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-uNOQt4tO14qBFWXenF0MLC4cqo3dv8qiHPGyjCl1rwOT0LomSHpcElbjjVh5CwzElInB38HD8aSRVugKQjeyHA==",
|
||||||
|
"requires": {
|
||||||
|
"fault": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remark-parse": {
|
||||||
|
"version": "8.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz",
|
||||||
|
"integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==",
|
||||||
|
"requires": {
|
||||||
|
"ccount": "^1.0.0",
|
||||||
|
"collapse-white-space": "^1.0.2",
|
||||||
|
"is-alphabetical": "^1.0.0",
|
||||||
|
"is-decimal": "^1.0.0",
|
||||||
|
"is-whitespace-character": "^1.0.0",
|
||||||
|
"is-word-character": "^1.0.0",
|
||||||
|
"markdown-escapes": "^1.0.0",
|
||||||
|
"parse-entities": "^2.0.0",
|
||||||
|
"repeat-string": "^1.5.4",
|
||||||
|
"state-toggle": "^1.0.0",
|
||||||
|
"trim": "0.0.1",
|
||||||
|
"trim-trailing-lines": "^1.0.0",
|
||||||
|
"unherit": "^1.0.4",
|
||||||
|
"unist-util-remove-position": "^2.0.0",
|
||||||
|
"vfile-location": "^3.0.0",
|
||||||
|
"xtend": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remark-stringify": {
|
||||||
|
"version": "8.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.1.tgz",
|
||||||
|
"integrity": "sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A==",
|
||||||
|
"requires": {
|
||||||
|
"ccount": "^1.0.0",
|
||||||
|
"is-alphanumeric": "^1.0.0",
|
||||||
|
"is-decimal": "^1.0.0",
|
||||||
|
"is-whitespace-character": "^1.0.0",
|
||||||
|
"longest-streak": "^2.0.1",
|
||||||
|
"markdown-escapes": "^1.0.0",
|
||||||
|
"markdown-table": "^2.0.0",
|
||||||
|
"mdast-util-compact": "^2.0.0",
|
||||||
|
"parse-entities": "^2.0.0",
|
||||||
|
"repeat-string": "^1.5.4",
|
||||||
|
"state-toggle": "^1.0.0",
|
||||||
|
"stringify-entities": "^3.0.0",
|
||||||
|
"unherit": "^1.0.4",
|
||||||
|
"xtend": "^4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repeat-string": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||||
|
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
|
||||||
|
},
|
||||||
|
"replace-ext": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs="
|
||||||
|
},
|
||||||
|
"sprintf-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||||
|
},
|
||||||
|
"state-toggle": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ=="
|
||||||
|
},
|
||||||
|
"stringify-entities": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg==",
|
||||||
|
"requires": {
|
||||||
|
"character-entities-html4": "^1.0.0",
|
||||||
|
"character-entities-legacy": "^1.0.0",
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"to-vfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==",
|
||||||
|
"requires": {
|
||||||
|
"is-buffer": "^2.0.0",
|
||||||
|
"vfile": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"trim": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0="
|
||||||
|
},
|
||||||
|
"trim-trailing-lines": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA=="
|
||||||
|
},
|
||||||
|
"trough": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA=="
|
||||||
|
},
|
||||||
|
"unherit": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.0",
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unified": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==",
|
||||||
|
"requires": {
|
||||||
|
"bail": "^1.0.0",
|
||||||
|
"extend": "^3.0.0",
|
||||||
|
"is-buffer": "^2.0.0",
|
||||||
|
"is-plain-obj": "^2.0.0",
|
||||||
|
"trough": "^1.0.0",
|
||||||
|
"vfile": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unist-util-is": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ=="
|
||||||
|
},
|
||||||
|
"unist-util-remove-position": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==",
|
||||||
|
"requires": {
|
||||||
|
"unist-util-visit": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unist-util-stringify-position": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
|
||||||
|
"requires": {
|
||||||
|
"@types/unist": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unist-util-visit": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==",
|
||||||
|
"requires": {
|
||||||
|
"@types/unist": "^2.0.0",
|
||||||
|
"unist-util-is": "^4.0.0",
|
||||||
|
"unist-util-visit-parents": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unist-util-visit-parents": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/unist": "^2.0.0",
|
||||||
|
"unist-util-is": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vfile": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-a/alcwCvtuc8OX92rqqo7PflxiCgXRFjdyoGVuYV+qbgCb0GgZJRvIgCD4+U/Kl1yhaRsaTwksF88xbPyGsgpw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/unist": "^2.0.0",
|
||||||
|
"is-buffer": "^2.0.0",
|
||||||
|
"replace-ext": "1.0.0",
|
||||||
|
"unist-util-stringify-position": "^2.0.0",
|
||||||
|
"vfile-message": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vfile-location": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g=="
|
||||||
|
},
|
||||||
|
"vfile-message": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/unist": "^2.0.0",
|
||||||
|
"unist-util-stringify-position": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xtend": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
tools/formatter/package.json
Normal file
21
tools/formatter/package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "fcc-translation-annotation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "translation-annotation/annotate.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"js-yaml": "^3.14.0",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
|
"mdast-builder": "^1.1.1",
|
||||||
|
"remark-frontmatter": "^2.0.0",
|
||||||
|
"remark-parse": "^8.0.3",
|
||||||
|
"remark-stringify": "^8.1.1",
|
||||||
|
"to-vfile": "^6.1.0",
|
||||||
|
"unified": "^9.2.0"
|
||||||
|
}
|
||||||
|
}
|
20
tools/formatter/translation-annotation/annotate.js
Normal file
20
tools/formatter/translation-annotation/annotate.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const { getText } = require('./get-challenge-text');
|
||||||
|
const { challengeToString } = require('./create-challenge-string');
|
||||||
|
const { parseMD } = require('../../challenge-md-parser/mdx');
|
||||||
|
|
||||||
|
module.exports.annotate = async function annotate(filePath) {
|
||||||
|
return generateTranscribableChallenge(filePath)
|
||||||
|
.then(challengeToString)
|
||||||
|
.catch(err => {
|
||||||
|
console.log('Error transforming');
|
||||||
|
console.log(filePath);
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async function generateTranscribableChallenge(fullPath) {
|
||||||
|
return Promise.all([parseMD(fullPath), getText(fullPath)]).then(results => ({
|
||||||
|
...results[0],
|
||||||
|
...results[1]
|
||||||
|
}));
|
||||||
|
}
|
@ -0,0 +1,113 @@
|
|||||||
|
const { pick } = require('lodash');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
|
const frontmatterProperties = [
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'challengeType',
|
||||||
|
'videoId',
|
||||||
|
'videoUrl',
|
||||||
|
'forumTopicId',
|
||||||
|
'isPrivate',
|
||||||
|
'required',
|
||||||
|
'helpCategory'
|
||||||
|
];
|
||||||
|
|
||||||
|
const otherProperties = [
|
||||||
|
'description',
|
||||||
|
'instructions',
|
||||||
|
'tests',
|
||||||
|
'solutions',
|
||||||
|
'files',
|
||||||
|
'question'
|
||||||
|
];
|
||||||
|
|
||||||
|
const notranslateStart = '<notranslate>';
|
||||||
|
const notranslateEnd = '</notranslate>';
|
||||||
|
|
||||||
|
function createFrontmatter(data) {
|
||||||
|
Object.keys(data).forEach(key => {
|
||||||
|
if (!frontmatterProperties.includes(key) && !otherProperties.includes(key))
|
||||||
|
throw Error(`Unknown property '${key}'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: sort the keys? It doesn't matter from a machine perspective, but
|
||||||
|
// it does from human-readability one. We could get lucky and have the order
|
||||||
|
// be preserved accidentally.
|
||||||
|
const frontData = pick(data, frontmatterProperties);
|
||||||
|
const frontYAML = yaml.dump(frontData);
|
||||||
|
|
||||||
|
return `---
|
||||||
|
${frontYAML}---
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: trimEnd is used since trailing whitespace is rarely used (it can create
|
||||||
|
// a <br>, but that's uncommon and hard to read)
|
||||||
|
function createHints({ tests }) {
|
||||||
|
if (!tests) return '';
|
||||||
|
const strTests = tests
|
||||||
|
.map(
|
||||||
|
({ text, testString }) => `${text.trimEnd()}
|
||||||
|
|
||||||
|
${notranslateStart}
|
||||||
|
${testString.trimEnd()}
|
||||||
|
${notranslateEnd}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
return createSection('hints', strTests);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createQuestion({ question }) {
|
||||||
|
if (!question) return '';
|
||||||
|
const { text, answers, solution } = question;
|
||||||
|
const formattedAnswers = answers.map(
|
||||||
|
answer => `${notranslateStart}
|
||||||
|
${answer.trimEnd()}
|
||||||
|
${notranslateEnd}
|
||||||
|
`
|
||||||
|
).join(`
|
||||||
|
---
|
||||||
|
|
||||||
|
`);
|
||||||
|
const formattedQuestion =
|
||||||
|
createSection('text', text, { depth: 2 }) +
|
||||||
|
createSection('answers', formattedAnswers, { depth: 2 }) +
|
||||||
|
createSection('video-solution', solution, { depth: 2, translate: false });
|
||||||
|
return createSection('question', formattedQuestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInstructions({ instructions }) {
|
||||||
|
return createSection('instructions', instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDescription({ description }) {
|
||||||
|
return createSection('description', description);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSection(heading, contents, options) {
|
||||||
|
const { depth = 1, translate = true } = options || {};
|
||||||
|
return contents && contents.toString().trim()
|
||||||
|
? `${notranslateStart}
|
||||||
|
${''.padEnd(depth, '#')} --${heading}--
|
||||||
|
${translate ? notranslateEnd + '\n' : ''}
|
||||||
|
${contents.toString().trimEnd()}
|
||||||
|
${translate ? '' : notranslateEnd + '\n'}
|
||||||
|
`
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function challengeToString(data) {
|
||||||
|
const chalString =
|
||||||
|
createFrontmatter(data) +
|
||||||
|
createDescription(data) +
|
||||||
|
createInstructions(data) +
|
||||||
|
createQuestion(data) +
|
||||||
|
createHints(data);
|
||||||
|
// all sections have a trailing '\n', the last one of which needs removing
|
||||||
|
return chalString.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.challengeToString = challengeToString;
|
26
tools/formatter/translation-annotation/get-challenge-text.js
Normal file
26
tools/formatter/translation-annotation/get-challenge-text.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const unified = require('unified');
|
||||||
|
const vfile = require('to-vfile');
|
||||||
|
const markdown = require('remark-parse');
|
||||||
|
const frontmatter = require('remark-frontmatter');
|
||||||
|
|
||||||
|
const textToData = require('./plugins/text-to-data');
|
||||||
|
const testsToData = require('./plugins/tests-to-data');
|
||||||
|
const questionToData = require('./plugins/question-to-data');
|
||||||
|
|
||||||
|
const textProcessor = unified()
|
||||||
|
.use(markdown)
|
||||||
|
.use(textToData)
|
||||||
|
.use(testsToData)
|
||||||
|
.use(questionToData)
|
||||||
|
.use(frontmatter, ['yaml']);
|
||||||
|
|
||||||
|
exports.getText = createProcessor(textProcessor);
|
||||||
|
|
||||||
|
function createProcessor(processor) {
|
||||||
|
return async msg => {
|
||||||
|
const file = typeof msg === 'string' ? vfile.readSync(msg) : msg;
|
||||||
|
const tree = processor.parse(file);
|
||||||
|
await processor.run(tree, file);
|
||||||
|
return file.data;
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
const { root } = require('mdast-builder');
|
||||||
|
const getAllBetween = require('../../../challenge-md-parser/mdx/plugins/utils/between-headings');
|
||||||
|
const {
|
||||||
|
splitOnThematicBreak
|
||||||
|
} = require('../../../challenge-md-parser/mdx/plugins/utils/split-on-thematic-break');
|
||||||
|
|
||||||
|
const { stringifyMd } = require('./text-to-data');
|
||||||
|
|
||||||
|
function plugin() {
|
||||||
|
return transformer;
|
||||||
|
|
||||||
|
function transformer(tree, file) {
|
||||||
|
const questionNodes = getAllBetween(tree, '--question--');
|
||||||
|
if (questionNodes.length > 0) {
|
||||||
|
const questionTree = root(questionNodes);
|
||||||
|
const textNodes = getAllBetween(questionTree, '--text--');
|
||||||
|
const answersNodes = getAllBetween(questionTree, '--answers--');
|
||||||
|
const solutionNodes = getAllBetween(questionTree, '--video-solution--');
|
||||||
|
|
||||||
|
const question = getQuestion(textNodes, answersNodes, solutionNodes);
|
||||||
|
|
||||||
|
file.data.question = question;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQuestion(textNodes, answersNodes, solutionNodes) {
|
||||||
|
const text = stringifyMd(textNodes);
|
||||||
|
const answers = getAnswers(answersNodes);
|
||||||
|
const solution = getSolution(solutionNodes);
|
||||||
|
|
||||||
|
if (!text) throw Error('text is missing from question');
|
||||||
|
if (!answers) throw Error('answers are missing from question');
|
||||||
|
if (!solution) throw Error('solution is missing from question');
|
||||||
|
|
||||||
|
return { text, answers, solution };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAnswers(answersNodes) {
|
||||||
|
const answerGroups = splitOnThematicBreak(answersNodes);
|
||||||
|
return answerGroups.map(answer => stringifyMd(answer));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSolution(solutionNodes) {
|
||||||
|
let solution;
|
||||||
|
try {
|
||||||
|
if (solutionNodes.length > 1) throw Error('Too many nodes');
|
||||||
|
if (solutionNodes[0].children.length > 1)
|
||||||
|
throw Error('Too many child nodes');
|
||||||
|
const solutionString = solutionNodes[0].children[0].value;
|
||||||
|
if (solutionString === '') throw Error('Non-empty string required');
|
||||||
|
|
||||||
|
solution = Number(solutionString);
|
||||||
|
if (Number.isNaN(solution)) throw Error('Not a number');
|
||||||
|
if (solution < 1) throw Error('Not positive number');
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
throw Error('A video solution should be a positive integer');
|
||||||
|
}
|
||||||
|
|
||||||
|
return solution;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = plugin;
|
@ -0,0 +1,32 @@
|
|||||||
|
const chunk = require('lodash/chunk');
|
||||||
|
const getAllBetween = require('../../../challenge-md-parser/mdx/plugins/utils/between-headings');
|
||||||
|
const { stringifyMd } = require('./text-to-data');
|
||||||
|
|
||||||
|
function plugin() {
|
||||||
|
return transformer;
|
||||||
|
|
||||||
|
function transformer(tree, file) {
|
||||||
|
const hintNodes = getAllBetween(tree, '--hints--');
|
||||||
|
if (hintNodes.length % 2 !== 0)
|
||||||
|
throw Error('Tests must be in (text, ```testString```) order');
|
||||||
|
|
||||||
|
const tests = chunk(hintNodes, 2).map(getTest);
|
||||||
|
file.data.tests = tests;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTest(hintNodes) {
|
||||||
|
const [textNode, testStringNode] = hintNodes;
|
||||||
|
const text = stringifyMd([textNode]);
|
||||||
|
const testString = stringifyMd([testStringNode]);
|
||||||
|
|
||||||
|
if (!text) throw Error('text is missing from hint');
|
||||||
|
// stub tests (i.e. text, but no testString) are allowed, but the md must
|
||||||
|
// have a code block, even if it is empty.
|
||||||
|
if (!testString && testString !== '')
|
||||||
|
throw Error('testString (code block) is missing from hint');
|
||||||
|
|
||||||
|
return { text, testString };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = plugin;
|
@ -0,0 +1,26 @@
|
|||||||
|
const stringify = require('remark-stringify');
|
||||||
|
const { root } = require('mdast-builder');
|
||||||
|
const unified = require('unified');
|
||||||
|
const getAllBetween = require('../../../challenge-md-parser/mdx/plugins/utils/between-headings');
|
||||||
|
|
||||||
|
const stringifyMd = nodes =>
|
||||||
|
unified()
|
||||||
|
.use(stringify, { fences: true, emphasis: '*' })
|
||||||
|
.stringify(root(nodes));
|
||||||
|
|
||||||
|
// NOTE: we need a new plugin (rather than using the challenge parser's plugin)
|
||||||
|
// simply because it adds html to the descriptions. It's easier to start from
|
||||||
|
// scratch.
|
||||||
|
function plugin() {
|
||||||
|
return transformer;
|
||||||
|
|
||||||
|
function transformer(tree, file) {
|
||||||
|
file.data.description = stringifyMd(getAllBetween(tree, '--description--'));
|
||||||
|
file.data.instructions = stringifyMd(
|
||||||
|
getAllBetween(tree, '--instructions--')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = plugin;
|
||||||
|
module.exports.stringifyMd = stringifyMd;
|
Loading…
x
Reference in New Issue
Block a user