diff --git a/.gitignore b/.gitignore index 786b71a..6d6c441 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,4 @@ bin/configlet.zip node_modules tmp -exercises/**/.gitignore lib diff --git a/Makefile b/Makefile index ab6cf62..61d83fe 100644 --- a/Makefile +++ b/Makefile @@ -4,29 +4,56 @@ 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)"; \ @@ -34,6 +61,7 @@ copy-exercise: 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 @@ -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 \ @@ -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) @@ -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 \ No newline at end of file diff --git a/bin/add-practice-exercise b/bin/add-practice-exercise index 7c86306..6283caf 100755 --- a/bin/add-practice-exercise +++ b/bin/add-practice-exercise @@ -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: diff --git a/templates/.gitignore b/templates/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/templates/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/templates/rescript.json b/templates/rescript.json new file mode 100644 index 0000000..3db8c50 --- /dev/null +++ b/templates/rescript.json @@ -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"] +} diff --git a/templates/testTemplate.js b/templates/testTemplate.js new file mode 100644 index 0000000..0db698e --- /dev/null +++ b/templates/testTemplate.js @@ -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) \ No newline at end of file