Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ bin/configlet.zip
node_modules

tmp
exercises/**/.gitignore
lib
68 changes: 49 additions & 19 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,64 @@ EXERCISE ?= ""
EXERCISES = $(shell find ./exercises/practice -maxdepth 1 -mindepth 1 -type d | cut -s -d '/' -f4 | sort)
OUTDIR ?= "tmp"

# check all package.json and package-lock.json are matching
check-package-files:
@echo "Validation package.json files..."
@for pkg in $(PKG_FILES); do \
! ./bin/md5-hash $$pkg | grep -qv $(SOURCE_PKG_MD5) || { echo "$$pkg does not match main package.json. Please run 'make sync-package-files' locally and commit the results."; exit 1; }; \
done
@echo "Validation package-lock.json files..."
@for pkg in $(PKG_LOCK_FILES); do \
! ./bin/md5-hash $$pkg | grep -qv $(SOURCE_PKG_LOCK_MD5) || { echo "$$pkg does not match main package.json. Please run 'make sync-package-files' locally and commit the results."; exit 1; }; \
# Define the files you want to ensure are synced across all exercises
FILES_TO_CHECK = package.json package-lock.json rescript.json .gitignore LICENSE .meta/testTemplate.js

# check all exercise files that need to be in sync
check-exercise-files:
@for exercise in $(EXERCISES); do \
echo "Checking exercise: $$exercise"; \
for file in $(FILES_TO_CHECK); do \
target="exercises/practice/$$exercise/$$file"; \
\
# Map the source template path \
if [ "$$file" = ".meta/testTemplate.js" ]; then \
source="./templates/testTemplate.js"; \
elif [ -f "./templates/$$file" ]; then \
source="./templates/$$file"; \
else \
source="./$$file"; \
fi; \
\
# 1. Check if the file exists \
if [ ! -f "$$target" ]; then \
echo "ERROR: Missing file $$file in $$exercise. Run make sync-exercise-files and commit the changes."; \
exit 1; \
fi; \
\
# 2. Check if the content matches (ignoring name/version) \
diff -q -I '"name":' -I '"version":' "$$source" "$$target" > /dev/null || { \
echo "ERROR: $$target does not match template $$source."; \
diff -u -I '"name":' -I '"version":' "$$source" "$$target" | head -n 20; \
exit 1; \
}; \
done; \
done
@echo "package-file check complete..."
@echo "All exercises contain all required files and are in sync."

# copy package.json and package-lock.json for single exercise
copy-package-file:
# copy all relevant files for a single exercise - test template, config etc.
copy-exercise-files:
@cp package.json exercises/practice/$(EXERCISE)/package.json
@cp package-lock.json exercises/practice/$(EXERCISE)/package-lock.json
@cp rescript.json exercises/practice/$(EXERCISE)/rescript.json

# copy package files to all exercise directories
sync-package-files:
@echo "Syncing package.json and package-lock.json..."
@for exercise in $(EXERCISES); do EXERCISE=$$exercise $(MAKE) -s copy-package-file || exit 1; done
@cp templates/rescript.json exercises/practice/$(EXERCISE)/rescript.json
@cp templates/.gitignore exercises/practice/$(EXERCISE)/.gitignore
@cp LICENSE exercises/practice/$(EXERCISE)/LICENSE
@cp templates/testTemplate.js exercises/practice/$(EXERCISE)/.meta/testTemplate.js

# sync all files for each exercise directory
sync-exercise-files:
@echo "Syncing exercise files..."
@for exercise in $(EXERCISES); do EXERCISE=$$exercise $(MAKE) -s copy-exercise-files || exit 1; done

# copy single exercise build artifacts for testing
copy-exercise:
if [ -f exercises/practice/$(EXERCISE)/src/*.res ]; then \
echo "Copying $(EXERCISE)"; \
cp exercises/practice/$(EXERCISE)/.meta/*.res $(OUTDIR)/src/; \
cp exercises/practice/$(EXERCISE)/tests/*.res $(OUTDIR)/tests/; \
fi

# copy build artifacts for testing
copy-all-exercises:
@echo "Copying exercises for testing..."
@mkdir -p $(OUTDIR)/src
Expand All @@ -50,6 +78,7 @@ format:
@echo "Formatting ReScript files..."
@find . -name "node_modules" -prune -o -name "*.res" -print -o -name "*.resi" -print | xargs npx rescript format

# Generate tests for all exercises
generate-tests:
@echo "Generating tests for all exercises..."
@for exercise in $(EXERCISES); do \
Expand All @@ -62,6 +91,7 @@ generate-tests:
done
@echo "All tests generated successfully."

# Generate test for exercise
generate-test:
ifeq ($(EXERCISE),"")
$(error EXERCISE variable is required. usage: make generate_test EXERCISE=hello-world)
Expand All @@ -70,6 +100,6 @@ endif

test:
$(MAKE) -s clean
$(MAKE) -s check-package-files
$(MAKE) -s check-exercise-files
$(MAKE) -s copy-all-exercises
npm run ci
2 changes: 2 additions & 0 deletions bin/add-practice-exercise
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ fi
exercise_dir="exercises/practice/${slug}"
files=$(jq -r --arg dir "${exercise_dir}" '.files | to_entries | map({key: .key, value: (.value | map("'"'"'" + $dir + "/" + . + "'"'"'") | join(" and "))}) | from_entries' "${exercise_dir}/.meta/config.json")

make copy-exercise-files EXERCISE="${slug}"

cat << NEXT_STEPS

Your next steps are:
Expand Down
1 change: 1 addition & 0 deletions templates/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
15 changes: 15 additions & 0 deletions templates/rescript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "@exercism/rescript",
"sources": [
{ "dir": "src", "subdirs": true, "type": "dev" },
{ "dir": "tests", "subdirs": true, "type": "dev" }
],
"package-specs": [
{
"module": "esmodule",
"in-source": true
}
],
"suffix": ".res.js",
"dev-dependencies": ["rescript-test"]
}
17 changes: 17 additions & 0 deletions templates/testTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { stringEqual } from "../../../../test_generator/assertions.js";
import { generateTests } from '../../../../test_generator/testGenerator.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const slug = path.basename(path.resolve(__dirname, '..'))

// EDIT THIS WITH YOUR ASSERTIONS
export const assertionFunctions = [ stringEqual ]

// EDIT THIS WITH YOUR TEST TEMPLATES
export const template = (c) => {
return `stringEqual(~message="${c.description}", hello(), "${c.expected}")`
}

generateTests(__dirname, slug, assertionFunctions, template)