chore: import upstream snapshot with attribution
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
i18n - Build Validation / Validate i18n Builds (24) (push) Has been cancelled
CI - Node.js / Lint (24) (push) Has been cancelled
CI - Node.js / Build (24) (push) Has been cancelled
CI - Node.js / Test (24) (push) Has been cancelled
CI - Node.js / Test - Upcoming Changes (24) (push) Has been cancelled
CI - Node.js / Test - i18n (italian, 24) (push) Has been cancelled
CI - Node.js / Test - i18n (portuguese, 24) (push) Has been cancelled
CD - Docker - GHCR Images / Build and Push Images (push) Has been cancelled
This commit is contained in:
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Define the source file
|
||||
source_file="curriculum/challenges/english/22-rosetta-code/rosetta-code-challenges/execute-brain.md"
|
||||
|
||||
# Define the destination directory
|
||||
destination_dir="curriculum/challenges"
|
||||
|
||||
# Loop through all directories except _meta and english
|
||||
for dir in $(find "$destination_dir" -mindepth 1 -maxdepth 1 -type d ! -name '_meta' ! -name 'english'); do
|
||||
# Copy the file to each directory
|
||||
cp "./$source_file" "./$dir/22-rosetta-code/rosetta-code-challenges/"
|
||||
done
|
||||
@@ -0,0 +1,36 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/* This can be used to create NGINX maps for redirects. After running this
|
||||
script with `npx tsx redirect-gen`, the map should appear in the current
|
||||
directory.
|
||||
*/
|
||||
|
||||
function createRedirectMap(): void {
|
||||
const basePath = '../../../curriculum/challenges/english/18-project-euler';
|
||||
const directories = fs.readdirSync(path.resolve(__dirname, basePath));
|
||||
|
||||
let mapObject = '';
|
||||
|
||||
for (let i = 0; i < directories.length; i++) {
|
||||
const files = fs.readdirSync(
|
||||
path.resolve(__dirname, `${basePath}/${directories[i]}`)
|
||||
);
|
||||
|
||||
for (let j = 0; j < files.length; j++) {
|
||||
const fileName = path.parse(files[j]).name;
|
||||
mapObject += `~^/learn/coding-interview-prep/project-euler/${fileName}/?$ /learn/project-euler/${directories[i]}/${fileName}; \n`;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFile('redirectMap.map', mapObject, 'utf8', function (err) {
|
||||
if (err) {
|
||||
console.log('An error occurred while writing MAP redirect file', err);
|
||||
return console.log(err);
|
||||
}
|
||||
|
||||
console.log('Map file has been saved.');
|
||||
});
|
||||
}
|
||||
|
||||
createRedirectMap();
|
||||
@@ -0,0 +1,33 @@
|
||||
## WARNING: Never change any of the ID's or delete anything. Mark things as deprecated instead.
|
||||
|
||||
### How to create a new exam:
|
||||
|
||||
1. Add the exam file in the `exams` folder
|
||||
|
||||
2. Add the exam meta data:
|
||||
|
||||
- `_id`: This should match the `id` in the exam challenge markdown file,
|
||||
- `title`: This should match the `title` in the exam challenge markdown file,
|
||||
- `numberOfQuestionsInExam`: This is how many questions will be given to campers who take the exam. It must be less than or equal to the length of the `questions` array.
|
||||
- `passingPercent`: Percent of questions needed to get correct to pass the exam
|
||||
- `prerequisites`: Array of challenges that are required to complete before being able to take the exam. Each should have an `id` and `title` that match what's in their challenge markdown file
|
||||
- `questions`: Array of exam questions
|
||||
|
||||
3. Add the exam questions to the `questions` array:
|
||||
|
||||
- `question`: The exam question
|
||||
- `wrongAnswers`: Array of answers. There should be at least 4, but 6-7 would be ideal.
|
||||
- `correctAnswers`: Array of answers. There should be at least 1, but 2 or more would be ideal when possible.
|
||||
|
||||
4. `wrongAnswers` and `correctAnswers` arrays:
|
||||
|
||||
- `answer`: This is one of the multiple choice options
|
||||
|
||||
5. Add the ID's:
|
||||
|
||||
- Change the `examPath` variable in the `add-nano-ids.js` file to the name of the new exam file
|
||||
- Run it with `node add-nano-ids.js`. It will add an `id` to each `question`, and each `answer`.
|
||||
|
||||
Add a `deprecated: true` property to any of the `questions`, `wrongAnswers`, or `correctAnswers`. Any that include this will be omitted when generating an exam.
|
||||
|
||||
The exam files in this folder are not used in production. Never push real exam questions to GitHub or anywhere public. These exams are for local development and testing. To seed the real exams to staging/production databases, replace the example exams here with the real exams, connect to the desired database, and run the `create-exams.js` script.
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Script that will add `id` fields to the `questions`, `wrong_answers`, and
|
||||
* `correct_answers` of an exam file where the `id` doesn't exist.
|
||||
*/
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
const newId = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10);
|
||||
|
||||
// Change this to the path of the file you want to add id's to
|
||||
const examPath = './exams/example-certification-exam.yml';
|
||||
const examFile = readFileSync(examPath, { encoding: 'utf-8' });
|
||||
const examJson = yaml.load(examFile);
|
||||
|
||||
// The strangeness of how the id's are added below is to keep the id at the top
|
||||
// of each object when rewriting the YAML at the end
|
||||
const newQuestions = [];
|
||||
examJson.questions?.forEach(question => {
|
||||
let newQuestion;
|
||||
|
||||
if (!question.id) {
|
||||
newQuestion = { id: newId(), ...question };
|
||||
} else {
|
||||
newQuestion = { ...question };
|
||||
}
|
||||
|
||||
let newWrongAnswers = [];
|
||||
question.wrongAnswers?.forEach(answer => {
|
||||
if (!answer.id) {
|
||||
newWrongAnswers.push({ id: newId(), ...answer });
|
||||
} else {
|
||||
newWrongAnswers.push(answer);
|
||||
}
|
||||
});
|
||||
|
||||
let newCorrectAnswers = [];
|
||||
question.correctAnswers?.forEach(answer => {
|
||||
if (!answer.id) {
|
||||
newCorrectAnswers.push({ id: newId(), ...answer });
|
||||
} else {
|
||||
newCorrectAnswers.push(answer);
|
||||
}
|
||||
});
|
||||
|
||||
newQuestion.wrongAnswers = newWrongAnswers;
|
||||
newQuestion.correctAnswers = newCorrectAnswers;
|
||||
newQuestions.push(newQuestion);
|
||||
});
|
||||
|
||||
examJson.questions = newQuestions;
|
||||
|
||||
const yamlStr = yaml.dump(examJson);
|
||||
writeFileSync(examPath, yamlStr, 'utf8');
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import path, { join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import debug from 'debug';
|
||||
import dotenv from 'dotenv';
|
||||
import yaml from 'js-yaml';
|
||||
import { MongoClient, ObjectId } from 'mongodb';
|
||||
|
||||
import { validateExamSchema } from './exam-schema.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const log = debug('fcc:tools:seedExams');
|
||||
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
|
||||
const { MONGOHQ_URL } = process.env;
|
||||
|
||||
// Only these will be added to or updated in the database
|
||||
const examFilenames = [
|
||||
'foundational-c-sharp-with-microsoft-certification-exam.yml',
|
||||
'example-certification-exam.yml'
|
||||
];
|
||||
|
||||
const client = new MongoClient(MONGOHQ_URL);
|
||||
|
||||
function handleError(err, client) {
|
||||
if (err) {
|
||||
console.error('Oh noes!! Error seeding exams.');
|
||||
console.error(err);
|
||||
try {
|
||||
client.close();
|
||||
} catch {
|
||||
// no-op
|
||||
} finally {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const seed = async () => {
|
||||
await client.db('admin').command({ ping: 1 });
|
||||
log('Connected successfully to mongo');
|
||||
|
||||
const db = client.db('freecodecamp');
|
||||
const exams = db.collection('Exam');
|
||||
for (const filename of examFilenames) {
|
||||
try {
|
||||
const examPath = join(__dirname, 'exams', filename);
|
||||
const examFile = readFileSync(examPath, { encoding: 'utf-8' });
|
||||
const examJson = yaml.load(examFile);
|
||||
const validExam = validateExamSchema(examJson);
|
||||
|
||||
if (validExam.error) {
|
||||
throw new Error(
|
||||
`Invalid exam schema for '${filename}': ${validExam.error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
examJson._id = new ObjectId(examJson._id);
|
||||
|
||||
// Update existing database object, or create new if it doesn't exist
|
||||
await exams.updateOne(
|
||||
{ _id: examJson._id },
|
||||
{ $set: examJson },
|
||||
{ upsert: true }
|
||||
);
|
||||
|
||||
log(`'${examJson.title}' added to exams database.`);
|
||||
} catch (err) {
|
||||
handleError(err, client);
|
||||
}
|
||||
}
|
||||
|
||||
log('Finished seeding exams.');
|
||||
};
|
||||
|
||||
seed()
|
||||
.then(() => client.close())
|
||||
.catch(err => handleError(err, client));
|
||||
@@ -0,0 +1,13 @@
|
||||
import { configTypeChecked } from '@freecodecamp/eslint-config/base';
|
||||
import globals from 'globals';
|
||||
|
||||
export default [
|
||||
...configTypeChecked,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node // TODO: migrate to ESM and remove globals
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,86 @@
|
||||
import Joi from 'joi';
|
||||
import JoiObjectId from 'joi-objectid';
|
||||
|
||||
Joi.objectId = JoiObjectId(Joi);
|
||||
|
||||
const nanoIdRE = new RegExp('[a-z0-9]{10}');
|
||||
|
||||
const PrerequisitesJoi = Joi.object().keys({
|
||||
id: Joi.objectId().required(),
|
||||
title: Joi.string()
|
||||
});
|
||||
|
||||
const AnswerJoi = Joi.object().keys({
|
||||
id: Joi.string().regex(nanoIdRE).required(),
|
||||
deprecated: Joi.bool(),
|
||||
answer: Joi.string().required()
|
||||
});
|
||||
|
||||
const QuestionJoi = Joi.object().keys({
|
||||
id: Joi.string().regex(nanoIdRE).required(),
|
||||
question: Joi.string().required(),
|
||||
deprecated: Joi.bool(),
|
||||
wrongAnswers: Joi.array()
|
||||
.items(AnswerJoi)
|
||||
.required()
|
||||
.custom((value, helpers) => {
|
||||
const nonDeprecatedCount = value.reduce(
|
||||
(count, answer) => (answer.deprecated ? count : count + 1),
|
||||
0
|
||||
);
|
||||
const minimumAnswers = 4;
|
||||
|
||||
if (nonDeprecatedCount < minimumAnswers) {
|
||||
return helpers.message(
|
||||
`'wrongAnswers' must have at least ${minimumAnswers} non-deprecated answers.`
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
}),
|
||||
correctAnswers: Joi.array()
|
||||
.items(AnswerJoi)
|
||||
.required()
|
||||
.custom((value, helpers) => {
|
||||
const nonDeprecatedCount = value.reduce(
|
||||
(count, answer) => (answer.deprecated ? count : count + 1),
|
||||
0
|
||||
);
|
||||
const minimumAnswers = 1;
|
||||
|
||||
if (nonDeprecatedCount < minimumAnswers) {
|
||||
return helpers.message(
|
||||
`'correctAnswers' must have at least ${minimumAnswers} non-deprecated answer.`
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
});
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
// TODO: make sure _id and title match what's in the challenge markdown file
|
||||
_id: Joi.objectId().required(),
|
||||
title: Joi.string().required(),
|
||||
numberOfQuestionsInExam: Joi.number()
|
||||
.min(1)
|
||||
.max(
|
||||
Joi.ref('questions', {
|
||||
adjust: questions => {
|
||||
const nonDeprecatedCount = questions.reduce(
|
||||
(count, question) => (question.deprecated ? count : count + 1),
|
||||
0
|
||||
);
|
||||
return nonDeprecatedCount;
|
||||
}
|
||||
})
|
||||
)
|
||||
.required(),
|
||||
passingPercent: Joi.number().min(0).max(100).required(),
|
||||
prerequisites: Joi.array().items(PrerequisitesJoi),
|
||||
questions: Joi.array().items(QuestionJoi).min(1).required()
|
||||
});
|
||||
|
||||
export const validateExamSchema = exam => {
|
||||
return schema.validate(exam);
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
_id: 645147516c245de4d11eb7ba
|
||||
title: Example Certification Exam
|
||||
numberOfQuestionsInExam: 5
|
||||
passingPercent: 70
|
||||
questions:
|
||||
- id: vjxyn2ngzs
|
||||
question: Which of the following is NOT a JavaScript data type?
|
||||
wrongAnswers:
|
||||
- id: h1x269fts9
|
||||
answer: Integer
|
||||
- id: 3x3j6ts2sb
|
||||
answer: Boolean
|
||||
- id: 9g4ix2tr1z
|
||||
answer: Symbol
|
||||
- id: n1rfllrvvy
|
||||
answer: String
|
||||
- id: 33oq8q3aio
|
||||
answer: Bigint
|
||||
- id: 7w1gaddxup
|
||||
answer: Undefined
|
||||
- id: ywgqr2wp6i
|
||||
answer: Object
|
||||
correctAnswers:
|
||||
- id: ydmnlkjviv
|
||||
answer: Array
|
||||
- id: sy2trjs7p8
|
||||
answer: Float
|
||||
- id: lpmv63p5sv
|
||||
question: What does the `NaN` keyword represent in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: 3cxs9as4bt
|
||||
answer: Not a Node
|
||||
- id: axn8omt3zf
|
||||
answer: Null and None
|
||||
- id: 7ssyazt4zu
|
||||
answer: No Assignment Named
|
||||
- id: do4cm37x7y
|
||||
answer: Negative Assertion Needed
|
||||
- id: r2hbfnrodz
|
||||
answer: Never Allow Null
|
||||
correctAnswers:
|
||||
- id: ah5kkj38s1
|
||||
answer: Not a Number
|
||||
- id: g2hudvmgoj
|
||||
question: >-
|
||||
Which method is used to remove the last element from an array in
|
||||
JavaScript?
|
||||
wrongAnswers:
|
||||
- id: jrxd1hjv63
|
||||
answer: '`shift()`'
|
||||
- id: udc3kecrqn
|
||||
answer: '`unshift()`'
|
||||
- id: a12aysp2et
|
||||
answer: '`slice()`'
|
||||
- id: xljr0lkb0n
|
||||
answer: '`splice()`'
|
||||
- id: qe4z3kxgyz
|
||||
answer: '`concat()`'
|
||||
correctAnswers:
|
||||
- id: gwddf01p89
|
||||
answer: '`pop()`'
|
||||
- id: 7tfdjebrnq
|
||||
question: >-
|
||||
What is the correct way to create a new instance of an object in
|
||||
JavaScript?
|
||||
wrongAnswers:
|
||||
- id: 5bygwpaz4d
|
||||
answer: '`Object.create(MyObject);`'
|
||||
- id: h5s3304gbl
|
||||
answer: '`MyObject.create();`'
|
||||
- id: mdsjsjpi9t
|
||||
answer: '`Object.new(MyObject);`'
|
||||
- id: 3o8otd8j9h
|
||||
answer: '`MyObject.new();`'
|
||||
- id: h4ozml27zn
|
||||
answer: '`MyObject.instance();`'
|
||||
deprecated: true
|
||||
correctAnswers:
|
||||
- id: qbfhqmnedn
|
||||
answer: '`new MyObject();`'
|
||||
- id: hx0gxr5yjc
|
||||
question: What is the purpose of the `querySelectorAll` method in JavaScript?
|
||||
deprecated: true
|
||||
wrongAnswers:
|
||||
- id: 71iry42cy1
|
||||
answer: To select all elements with a specific ID
|
||||
- id: jjyk5e9e1l
|
||||
answer: To select all elements of a specific tag name
|
||||
- id: k5tpc2o2q8
|
||||
answer: To select all child elements of a specific parent
|
||||
- id: s3cm1tq4wt
|
||||
answer: To select all elements with a specific attribute
|
||||
- id: l1gtuheh87
|
||||
answer: To select elements based on their position in the DOM
|
||||
correctAnswers:
|
||||
- id: e6xz6zdpxr
|
||||
answer: To select all elements with a specific class name
|
||||
- id: eigcj3e0mg
|
||||
question: Which operator is used to compare two values for equality in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: ltgdcoyonb
|
||||
answer: '`!=`'
|
||||
- id: f3szfel7x6
|
||||
answer: '`<`'
|
||||
- id: b0meyxhvtu
|
||||
answer: '`>`'
|
||||
- id: 31dd0pte4i
|
||||
answer: '`<=`'
|
||||
- id: 9u8bi7wqa2
|
||||
answer: '`>=`'
|
||||
correctAnswers:
|
||||
- id: fmo9sof6v2
|
||||
answer: '`==`'
|
||||
deprecated: true
|
||||
- id: tl6eo06r4x
|
||||
answer: '`===`'
|
||||
- id: l5f2beg2ih
|
||||
question: >-
|
||||
Which of the following statements is used to terminate a loop in
|
||||
JavaScript?
|
||||
wrongAnswers:
|
||||
- id: kqi1fg8vtm
|
||||
answer: stop
|
||||
- id: a2clf7fmyx
|
||||
answer: exit
|
||||
- id: v58wx865e7
|
||||
answer: return
|
||||
- id: 5mgeaetnf0
|
||||
answer: halt
|
||||
- id: 2u73kyry6o
|
||||
answer: skip
|
||||
correctAnswers:
|
||||
- id: 7j046bkjmf
|
||||
answer: break
|
||||
- id: atuq2zwg4h
|
||||
question: What is the correct syntax to define a function in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: dusl68wx2o
|
||||
answer: '`function myFunction {}`'
|
||||
- id: dlwjt59a5s
|
||||
answer: '`def myFunction():`'
|
||||
- id: 98p8ai27ko
|
||||
answer: '`function = myFunction {}`'
|
||||
- id: 7ciq8sood9
|
||||
answer: '`function myFunction():`'
|
||||
correctAnswers:
|
||||
- id: wc3h7uc4zn
|
||||
answer: '`function myFunction() {}`'
|
||||
- id: 3ne0ju7yzq
|
||||
question: >-
|
||||
Which of the following methods is used to add a new element to the end of
|
||||
an array in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: fytb9yvbug
|
||||
answer: shift()
|
||||
- id: wnc31qc1ie
|
||||
answer: unshift()
|
||||
- id: 22mi7j64h5
|
||||
answer: pop()
|
||||
- id: da96ukvtol
|
||||
answer: splice()
|
||||
- id: gc6elnmtzw
|
||||
answer: concat()
|
||||
correctAnswers:
|
||||
- id: pymbxhyn12
|
||||
answer: push()
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
_id: 647e22d18acb466c97ccbef8
|
||||
title: Foundational C# with Microsoft Certification Exam
|
||||
numberOfQuestionsInExam: 5
|
||||
passingPercent: 75
|
||||
prerequisites:
|
||||
- id: 647f85d407d29547b3bee1bb
|
||||
title: Trophy - Write Your First Code Using C#
|
||||
- id: 647f87dc07d29547b3bee1bf
|
||||
title: Trophy - Create and Run Simple C# Console Applications
|
||||
- id: 647f882207d29547b3bee1c0
|
||||
title: Trophy - Add Logic to C# Console Applications
|
||||
- id: 647f867a07d29547b3bee1bc
|
||||
title: Trophy - Work with Variable Data in C# Console Applications
|
||||
- id: 647f877f07d29547b3bee1be
|
||||
title: Trophy - Create Methods in C# Console Applications
|
||||
- id: 647f86ff07d29547b3bee1bd
|
||||
title: Trophy - Debug C# Console Applications
|
||||
questions:
|
||||
- id: vjxyn2ngzs
|
||||
question: Which of the following is NOT a JavaScript data type?
|
||||
wrongAnswers:
|
||||
- id: 5y7xv9ppc1
|
||||
answer: Integer
|
||||
- id: 3x3j6ts2sb
|
||||
answer: Boolean
|
||||
- id: 9g4ix2tr1z
|
||||
answer: Symbol
|
||||
- id: n1rfllrvvy
|
||||
answer: String
|
||||
- id: 33oq8q3aio
|
||||
answer: Bigint
|
||||
- id: 7w1gaddxup
|
||||
answer: Undefined
|
||||
- id: ywgqr2wp6i
|
||||
answer: Object
|
||||
correctAnswers:
|
||||
- id: ydmnlkjviv
|
||||
answer: Array
|
||||
- id: sy2trjs7p8
|
||||
answer: Float
|
||||
- id: lpmv63p5sv
|
||||
question: What does the `NaN` keyword represent in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: 3cxs9as4bt
|
||||
answer: Not a Node
|
||||
- id: axn8omt3zf
|
||||
answer: Null and None
|
||||
- id: 7ssyazt4zu
|
||||
answer: No Assignment Named
|
||||
- id: do4cm37x7y
|
||||
answer: Negative Assertion Needed
|
||||
- id: r2hbfnrodz
|
||||
answer: Never Allow Null
|
||||
correctAnswers:
|
||||
- id: ah5kkj38s1
|
||||
answer: Not a Number
|
||||
- id: g2hudvmgoj
|
||||
question: >-
|
||||
Which method is used to remove the last element from an array in
|
||||
JavaScript?
|
||||
wrongAnswers:
|
||||
- id: jrxd1hjv63
|
||||
answer: '`shift()`'
|
||||
- id: udc3kecrqn
|
||||
answer: '`unshift()`'
|
||||
- id: a12aysp2et
|
||||
answer: '`slice()`'
|
||||
- id: xljr0lkb0n
|
||||
answer: '`splice()`'
|
||||
- id: qe4z3kxgyz
|
||||
answer: '`concat()`'
|
||||
correctAnswers:
|
||||
- id: gwddf01p89
|
||||
answer: '`pop()`'
|
||||
- id: 7tfdjebrnq
|
||||
question: >-
|
||||
What is the correct way to create a new instance of an object in
|
||||
JavaScript?
|
||||
wrongAnswers:
|
||||
- id: 5bygwpaz4d
|
||||
answer: '`Object.create(MyObject);`'
|
||||
- id: h5s3304gbl
|
||||
answer: '`MyObject.create();`'
|
||||
- id: mdsjsjpi9t
|
||||
answer: '`Object.new(MyObject);`'
|
||||
- id: 3o8otd8j9h
|
||||
answer: '`MyObject.new();`'
|
||||
- id: h4ozml27zn
|
||||
answer: '`MyObject.instance();`'
|
||||
deprecated: true
|
||||
correctAnswers:
|
||||
- id: qbfhqmnedn
|
||||
answer: '`new MyObject();`'
|
||||
- id: hx0gxr5yjc
|
||||
question: What is the purpose of the `querySelectorAll` method in JavaScript?
|
||||
deprecated: true
|
||||
wrongAnswers:
|
||||
- id: 71iry42cy1
|
||||
answer: To select all elements with a specific ID
|
||||
- id: jjyk5e9e1l
|
||||
answer: To select all elements of a specific tag name
|
||||
- id: k5tpc2o2q8
|
||||
answer: To select all child elements of a specific parent
|
||||
- id: s3cm1tq4wt
|
||||
answer: To select all elements with a specific attribute
|
||||
- id: l1gtuheh87
|
||||
answer: To select elements based on their position in the DOM
|
||||
correctAnswers:
|
||||
- id: e6xz6zdpxr
|
||||
answer: To select all elements with a specific class name
|
||||
- id: eigcj3e0mg
|
||||
question: Which operator is used to compare two values for equality in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: ltgdcoyonb
|
||||
answer: '`!=`'
|
||||
- id: f3szfel7x6
|
||||
answer: '`<`'
|
||||
- id: b0meyxhvtu
|
||||
answer: '`>`'
|
||||
- id: 31dd0pte4i
|
||||
answer: '`<=`'
|
||||
- id: 9u8bi7wqa2
|
||||
answer: '`>=`'
|
||||
correctAnswers:
|
||||
- id: fmo9sof6v2
|
||||
answer: '`==`'
|
||||
deprecated: true
|
||||
- id: tl6eo06r4x
|
||||
answer: '`===`'
|
||||
- id: l5f2beg2ih
|
||||
question: >-
|
||||
Which of the following statements is used to terminate a loop in
|
||||
JavaScript?
|
||||
wrongAnswers:
|
||||
- id: kqi1fg8vtm
|
||||
answer: stop
|
||||
- id: a2clf7fmyx
|
||||
answer: exit
|
||||
- id: v58wx865e7
|
||||
answer: return
|
||||
- id: 5mgeaetnf0
|
||||
answer: halt
|
||||
- id: 2u73kyry6o
|
||||
answer: skip
|
||||
correctAnswers:
|
||||
- id: 7j046bkjmf
|
||||
answer: break
|
||||
- id: atuq2zwg4h
|
||||
question: What is the correct syntax to define a function in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: dusl68wx2o
|
||||
answer: '`function myFunction {}`'
|
||||
- id: dlwjt59a5s
|
||||
answer: '`def myFunction():`'
|
||||
- id: 98p8ai27ko
|
||||
answer: '`function = myFunction {}`'
|
||||
- id: 7ciq8sood9
|
||||
answer: '`function myFunction():`'
|
||||
correctAnswers:
|
||||
- id: wc3h7uc4zn
|
||||
answer: '`function myFunction() {}`'
|
||||
- id: 3ne0ju7yzq
|
||||
question: >-
|
||||
Which of the following methods is used to add a new element to the end of
|
||||
an array in JavaScript?
|
||||
wrongAnswers:
|
||||
- id: fytb9yvbug
|
||||
answer: shift()
|
||||
- id: wnc31qc1ie
|
||||
answer: unshift()
|
||||
- id: 22mi7j64h5
|
||||
answer: pop()
|
||||
- id: da96ukvtol
|
||||
answer: splice()
|
||||
- id: gc6elnmtzw
|
||||
answer: concat()
|
||||
correctAnswers:
|
||||
- id: pymbxhyn12
|
||||
answer: push()
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@freecodecamp/seed-exams",
|
||||
"version": "0.0.1",
|
||||
"description": "Seed certification exams questions to database",
|
||||
"license": "BSD-3-Clause",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"pnpm": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --max-warnings 0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/freeCodeCamp/freeCodeCamp.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/freeCodeCamp/freeCodeCamp/issues"
|
||||
},
|
||||
"homepage": "https://github.com/freeCodeCamp/freeCodeCamp#readme",
|
||||
"author": "freeCodeCamp <team@freecodecamp.org>",
|
||||
"main": "none",
|
||||
"devDependencies": {
|
||||
"@freecodecamp/eslint-config": "workspace:*",
|
||||
"debug": "4.4.3",
|
||||
"dotenv": "16.6.1",
|
||||
"eslint": "^9.39.1",
|
||||
"joi": "17.13.3",
|
||||
"joi-objectid": "3.0.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"mongodb": "6.21.0",
|
||||
"nanoid": "4.0.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"super-block-a": {
|
||||
"blocks": {
|
||||
"block-one": {
|
||||
"meta": {},
|
||||
"challenges": [
|
||||
{
|
||||
"title": "Challenge One",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-a"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Two",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-a"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Three",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-a"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Four",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-a"
|
||||
}
|
||||
]
|
||||
},
|
||||
"block-two": {
|
||||
"meta": {},
|
||||
"challenges": [
|
||||
{
|
||||
"title": "Challenge Five",
|
||||
"block": "Block Two",
|
||||
"superBlock": "super-block-a"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Six",
|
||||
"block": "Block Two",
|
||||
"superBlock": "super-block-a"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Seven",
|
||||
"block": "Block Two",
|
||||
"superBlock": "super-block-a"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Eight",
|
||||
"block": "Block Two",
|
||||
"superBlock": "super-block-a"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"super-block-b": {
|
||||
"blocks": {
|
||||
"block-one": {
|
||||
"meta": {},
|
||||
"challenges": [
|
||||
{
|
||||
"title": "Challenge Nine",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-b"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Ten",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-b"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Eleven",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-b"
|
||||
},
|
||||
{
|
||||
"title": "Challenge Twelve",
|
||||
"block": "Block One",
|
||||
"superBlock": "super-block-b"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { configTypeChecked } from '@freecodecamp/eslint-config/base';
|
||||
import globals from 'globals';
|
||||
|
||||
export default [
|
||||
...configTypeChecked,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node // TODO: migrate to ESM and remove globals
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@freecodecamp/scripts-seed",
|
||||
"version": "0.0.1",
|
||||
"description": "The freeCodeCamp.org open-source codebase and curriculum",
|
||||
"license": "BSD-3-Clause",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=24",
|
||||
"pnpm": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --max-warnings 0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/freeCodeCamp/freeCodeCamp.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/freeCodeCamp/freeCodeCamp/issues"
|
||||
},
|
||||
"homepage": "https://github.com/freeCodeCamp/freeCodeCamp#readme",
|
||||
"author": "freeCodeCamp <team@freecodecamp.org>",
|
||||
"main": "none",
|
||||
"devDependencies": {
|
||||
"@freecodecamp/eslint-config": "workspace:*",
|
||||
"debug": "4.4.3",
|
||||
"dotenv": "16.6.1",
|
||||
"eslint": "^9.39.1",
|
||||
"mongodb": "6.21.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
const { parseArgs } = require('node:util');
|
||||
|
||||
const path = require('path');
|
||||
const debug = require('debug');
|
||||
const { MongoClient } = require('mongodb');
|
||||
|
||||
require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') });
|
||||
const {
|
||||
demoUser,
|
||||
blankUser,
|
||||
publicUser,
|
||||
fullyCertifiedUser,
|
||||
userIds,
|
||||
almostFullyCertifiedUser,
|
||||
unclaimedUser
|
||||
} = require('./user-data');
|
||||
|
||||
const options = {
|
||||
'set-true': { type: 'string', multiple: true },
|
||||
'top-contributor': { type: 'boolean' },
|
||||
'set-false': { type: 'string', multiple: true },
|
||||
'seed-trophy-challenges': { type: 'boolean' },
|
||||
'certified-user': { type: 'boolean' },
|
||||
'almost-certified-user': { type: 'boolean' },
|
||||
'unclaimed-user': { type: 'boolean' }
|
||||
};
|
||||
|
||||
const { values: argValues } = parseArgs({ options });
|
||||
|
||||
const log = debug('fcc:tools:seedLocalAuthUser');
|
||||
const { MONGOHQ_URL } = process.env;
|
||||
|
||||
function handleError(err, client) {
|
||||
if (err) {
|
||||
console.error('Oh noes!! Error seeding local auth user.');
|
||||
console.error(err);
|
||||
try {
|
||||
client.close();
|
||||
} catch {
|
||||
// no-op
|
||||
} finally {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const trophyChallenges = [
|
||||
{
|
||||
id: '647f85d407d29547b3bee1bb',
|
||||
solution:
|
||||
'https://learn.microsoft.com/api/gamestatus/achievements/learn.wwl.get-started-c-sharp-part-1.trophy?username=moT01&locale=en-us',
|
||||
completedDate: 1695064765244
|
||||
},
|
||||
{
|
||||
id: '647f87dc07d29547b3bee1bf',
|
||||
solution:
|
||||
'https://learn.microsoft.com/api/gamestatus/achievements/learn.wwl.get-started-c-sharp-part-2.trophy?username=moT01&locale=en-us',
|
||||
completedDate: 1695064900926
|
||||
},
|
||||
{
|
||||
id: '647f882207d29547b3bee1c0',
|
||||
solution:
|
||||
'https://learn.microsoft.com/api/gamestatus/achievements/learn.wwl.get-started-c-sharp-part-3.trophy?username=moT01&locale=en-us',
|
||||
completedDate: 1695064949460
|
||||
},
|
||||
{
|
||||
id: '647f867a07d29547b3bee1bc',
|
||||
solution:
|
||||
'https://learn.microsoft.com/api/gamestatus/achievements/learn.wwl.get-started-c-sharp-part-4.trophy?username=moT01&locale=en-us',
|
||||
completedDate: 1695064986634
|
||||
},
|
||||
{
|
||||
id: '647f877f07d29547b3bee1be',
|
||||
solution:
|
||||
'https://learn.microsoft.com/api/gamestatus/achievements/learn.wwl.get-started-c-sharp-part-5.trophy?username=moT01&locale=en-us',
|
||||
completedDate: 1695065026465
|
||||
},
|
||||
{
|
||||
id: '647f86ff07d29547b3bee1bd',
|
||||
solution:
|
||||
'https://learn.microsoft.com/api/gamestatus/achievements/learn.wwl.get-started-c-sharp-part-6.trophy?username=moT01&locale=en-us',
|
||||
completedDate: 1695065060157
|
||||
}
|
||||
];
|
||||
|
||||
[demoUser, blankUser, fullyCertifiedUser].forEach(user => {
|
||||
if (argValues['top-contributor']) {
|
||||
user.yearsTopContributor = ['2017', '2018', '2019'];
|
||||
}
|
||||
for (const key of argValues['set-false'] || []) {
|
||||
user[key] = false;
|
||||
}
|
||||
for (const key of argValues['set-true'] || []) {
|
||||
user[key] = true;
|
||||
}
|
||||
if (argValues['--seed-trophy-challenges']) {
|
||||
user.completedChallenges = trophyChallenges;
|
||||
}
|
||||
});
|
||||
|
||||
const client = new MongoClient(MONGOHQ_URL);
|
||||
|
||||
const db = client.db('freecodecamp');
|
||||
const user = db.collection('user');
|
||||
|
||||
const dropUserTokens = async function () {
|
||||
await db.collection('UserToken').deleteMany({
|
||||
userId: {
|
||||
$in: userIds
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const dropUsers = async function () {
|
||||
await db.collection('user').deleteMany({
|
||||
_id: {
|
||||
$in: userIds
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const run = async () => {
|
||||
await client.db('admin').command({ ping: 1 });
|
||||
log('Connected successfully to mongo');
|
||||
|
||||
await dropUserTokens();
|
||||
await dropUsers();
|
||||
if (argValues['certified-user']) {
|
||||
await user.insertOne(fullyCertifiedUser);
|
||||
} else if (argValues['almost-certified-user']) {
|
||||
await user.insertOne(almostFullyCertifiedUser);
|
||||
} else if (argValues['unclaimed-user']) {
|
||||
await user.insertOne(unclaimedUser);
|
||||
} else {
|
||||
await user.insertOne(demoUser);
|
||||
}
|
||||
|
||||
await user.insertOne(blankUser);
|
||||
await user.insertOne(publicUser);
|
||||
|
||||
log('local auth user seed complete');
|
||||
};
|
||||
|
||||
run()
|
||||
.then(() => client.close())
|
||||
.catch(err => handleError(err, client));
|
||||
@@ -0,0 +1,73 @@
|
||||
const path = require('path');
|
||||
const debug = require('debug');
|
||||
const { MongoClient, ObjectId } = require('mongodb');
|
||||
require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') });
|
||||
|
||||
const log = debug('fcc:tools:seedMsUsername');
|
||||
const { MONGOHQ_URL } = process.env;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const allowedArgs = ['--delete-only'];
|
||||
|
||||
// Check for invalid arguments
|
||||
args.forEach(arg => {
|
||||
if (!allowedArgs.includes(arg))
|
||||
throw new Error(
|
||||
`Invalid argument ${arg}. Allowed arguments are ${allowedArgs.join(', ')}`
|
||||
);
|
||||
});
|
||||
|
||||
function handleError(err, client) {
|
||||
if (err) {
|
||||
console.error('Oh noes!! Error seeding MS username.');
|
||||
console.error(err);
|
||||
try {
|
||||
client.close();
|
||||
} catch {
|
||||
// no-op
|
||||
} finally {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const msAccountId = new ObjectId('65785b25d4c5bd0565c0184d');
|
||||
|
||||
const certifiedUserAccount = {
|
||||
_id: msAccountId,
|
||||
userId: new ObjectId('5fa2db00a25c1c1fa49ce067'),
|
||||
ttl: 77760000000,
|
||||
msUsername: 'certifieduser'
|
||||
};
|
||||
|
||||
const client = new MongoClient(MONGOHQ_URL);
|
||||
|
||||
const run = async () => {
|
||||
await client.db('admin').command({ ping: 1 });
|
||||
log('Connected successfully to mongo');
|
||||
|
||||
const db = client.db('freecodecamp');
|
||||
const msUsername = db.collection('MsUsername');
|
||||
if (args.includes('--delete-only')) {
|
||||
await msUsername.deleteOne({
|
||||
_id: { $eq: msAccountId }
|
||||
});
|
||||
|
||||
log('MS username deleted');
|
||||
return;
|
||||
}
|
||||
|
||||
// Rewrite if the object exists, create new if it doesn't
|
||||
await msUsername.updateOne(
|
||||
{ _id: msAccountId },
|
||||
{ $set: certifiedUserAccount },
|
||||
{ upsert: true }
|
||||
);
|
||||
|
||||
log('MS username seeded');
|
||||
};
|
||||
|
||||
run()
|
||||
.then(() => client.close())
|
||||
.catch(err => handleError(err, client));
|
||||
@@ -0,0 +1,97 @@
|
||||
const path = require('path');
|
||||
const debug = require('debug');
|
||||
const { MongoClient, ObjectId } = require('mongodb');
|
||||
|
||||
const { userIds } = require('./user-data');
|
||||
require('dotenv').config({ path: path.resolve(__dirname, '../../../.env') });
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const allowedArgs = ['delete-only'];
|
||||
|
||||
// Check for invalid arguments
|
||||
args.forEach(arg => {
|
||||
if (!allowedArgs.includes(arg))
|
||||
throw new Error(
|
||||
`Invalid argument ${arg}. Allowed arguments are ${allowedArgs.join(', ')}`
|
||||
);
|
||||
});
|
||||
|
||||
const log = debug('fcc:tools:seedSurveyInfo');
|
||||
const { MONGOHQ_URL } = process.env;
|
||||
|
||||
function handleError(err, client) {
|
||||
if (err) {
|
||||
console.error('Oh noes!! Error seeding survey info.');
|
||||
console.error(err);
|
||||
try {
|
||||
client.close();
|
||||
} catch {
|
||||
// no-op
|
||||
} finally {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const surveyIds = [
|
||||
new ObjectId('651c5a2a5f9b639b584028bc'),
|
||||
new ObjectId('651c5a4c5f9b639b584028bd')
|
||||
];
|
||||
|
||||
const defaultUserSurvey = {
|
||||
_id: surveyIds[0],
|
||||
title: 'Foundational C# with Microsoft Survey',
|
||||
responses: [
|
||||
{
|
||||
question: 'Please describe your role:',
|
||||
response: 'Beginner developer (less than 2 years experience)'
|
||||
},
|
||||
{
|
||||
question:
|
||||
'Prior to this course, how experienced were you with .NET and C#?',
|
||||
response: 'Novice (no prior experience)'
|
||||
}
|
||||
],
|
||||
userId: new ObjectId('5bd30e0f1caf6ac3ddddddb5')
|
||||
};
|
||||
|
||||
const certifiedUserSurvey = {
|
||||
_id: surveyIds[1],
|
||||
title: 'Foundational C# with Microsoft Survey',
|
||||
responses: [
|
||||
{
|
||||
question: 'Please describe your role:',
|
||||
response: 'Experienced developer (more than 5 years experience)'
|
||||
},
|
||||
{
|
||||
question:
|
||||
'Prior to this course, how experienced were you with .NET and C#?',
|
||||
response: 'Expert'
|
||||
}
|
||||
],
|
||||
userId: new ObjectId('5fa2db00a25c1c1fa49ce067')
|
||||
};
|
||||
|
||||
const client = new MongoClient(MONGOHQ_URL);
|
||||
|
||||
const run = async () => {
|
||||
await client.db('admin').command({ ping: 1 });
|
||||
log('Connected successfully to mongo');
|
||||
|
||||
const db = client.db('freecodecamp');
|
||||
const survey = db.collection('Survey');
|
||||
|
||||
await survey.deleteMany({ userId: { $in: userIds } });
|
||||
log('Survey info deleted');
|
||||
|
||||
if (!args.includes('delete-only')) {
|
||||
await survey.insertOne(defaultUserSurvey);
|
||||
await survey.insertOne(certifiedUserSurvey);
|
||||
log('Survey info seeded');
|
||||
}
|
||||
};
|
||||
|
||||
run()
|
||||
.then(() => client.close())
|
||||
.catch(err => handleError(err, client));
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,104 @@
|
||||
import { exec } from 'child_process';
|
||||
import { readFile, readdir, stat, writeFile } from 'fs/promises';
|
||||
import { join, sep } from 'path';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const asyncExec = promisify(exec);
|
||||
|
||||
const loadDirectory = async (path: string): Promise<string[]> => {
|
||||
const files: string[] = [];
|
||||
const status = await stat(path);
|
||||
if (status.isDirectory()) {
|
||||
const filesInDir = await readdir(path);
|
||||
for (const file of filesInDir) {
|
||||
files.push(...(await loadDirectory(join(path, file))));
|
||||
}
|
||||
} else {
|
||||
files.push(path);
|
||||
}
|
||||
return files.filter(f => !f.includes('DS_Store'));
|
||||
};
|
||||
|
||||
const syncChallenges = async () => {
|
||||
const block = process.env.FCC_BLOCK;
|
||||
const ignore = ['.markdownlint.yaml', '_meta', 'english'];
|
||||
const basePath = join(process.cwd(), 'curriculum', 'challenges');
|
||||
const allLangs = await readdir(basePath);
|
||||
const filtered = allLangs.filter(lang => !ignore.includes(lang));
|
||||
// these will be paths
|
||||
const english = await loadDirectory(join(basePath, 'english'));
|
||||
for (const path of english) {
|
||||
await Promise.all(
|
||||
filtered.map(async lang => {
|
||||
if (block && !path.includes(block)) {
|
||||
return;
|
||||
}
|
||||
const targetPath = path.replace('english', lang);
|
||||
// we swallow the error here to detect if the file doesn't exist
|
||||
const status = await stat(targetPath).catch(() => null);
|
||||
if (!status) {
|
||||
const targetDir = targetPath.split(sep);
|
||||
targetDir.pop();
|
||||
console.log(`Syncing ${path.split('/english/')[1]}`);
|
||||
await asyncExec(
|
||||
`mkdir -p ${targetDir.join(sep)} && cp ${path} ${targetPath}`
|
||||
);
|
||||
}
|
||||
const englishContent = await readFile(path, 'utf-8');
|
||||
if (path.endsWith('LICENSE.txt')) {
|
||||
await writeFile(targetPath, englishContent, 'utf-8');
|
||||
return;
|
||||
}
|
||||
const langContent = await readFile(targetPath, 'utf-8');
|
||||
const engLines = englishContent.split('\n');
|
||||
const engId = engLines.find(l => l.startsWith('id'));
|
||||
const engSlug = engLines.find(l => l.startsWith('dashedName'));
|
||||
const langLines = langContent.split('\n');
|
||||
const langId = langLines.find(l => l.startsWith('id'));
|
||||
const langSlug = langLines.find(
|
||||
l => l.startsWith('dashedName') || l.startsWith('certification:')
|
||||
);
|
||||
if (!langSlug) {
|
||||
throw new Error(
|
||||
`Missing dashedName for ${targetPath}. Please add it so that it matches the English version.`
|
||||
);
|
||||
}
|
||||
if (!langId) {
|
||||
throw new Error(
|
||||
`Missing id for ${targetPath}. Please add it so that it matches the English version.`
|
||||
);
|
||||
}
|
||||
if (engId && engId !== langId) {
|
||||
langLines.splice(langLines.indexOf(langId), 1, engId);
|
||||
console.log(`Updating ID for ${targetPath}`);
|
||||
await writeFile(targetPath, langLines.join('\n'), 'utf-8');
|
||||
}
|
||||
if (engSlug && engSlug !== langSlug) {
|
||||
langLines.splice(langLines.indexOf(langSlug), 1, engSlug);
|
||||
console.log(`Updating dashed name for ${targetPath}`);
|
||||
await writeFile(targetPath, langLines.join('\n'), 'utf-8');
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
for (const lang of filtered) {
|
||||
const langPath = join(process.cwd(), 'curriculum', 'challenges', lang);
|
||||
const langFiles = await loadDirectory(langPath);
|
||||
for (const path of langFiles) {
|
||||
if (block && !path.includes(block)) {
|
||||
continue;
|
||||
}
|
||||
const engPath = path.replace(lang, 'english');
|
||||
// we don't want an error, only need to know that the file exists
|
||||
const existsEnglish = await stat(engPath).catch(() => null);
|
||||
if (!existsEnglish) {
|
||||
console.log(`Unlinking ${path.split(`/${lang}/`)[1]}`);
|
||||
await asyncExec(`rm ${path}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void (async () => {
|
||||
await syncChallenges();
|
||||
})();
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
echo "Enter the ids of the challenges you wish to test (separated by commas) and hit enter."
|
||||
read -r input
|
||||
|
||||
|
||||
IFS=',' challengeIDS=($input)
|
||||
|
||||
for challengeID in "${challengeIDS[@]}";
|
||||
do
|
||||
challengeID="$(tr -d ' ' <<< "$challengeID")"
|
||||
FCC_CHALLENGE_ID=$challengeID pnpm turbo -F=@freecodecamp/curriculum test
|
||||
done
|
||||
Reference in New Issue
Block a user