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
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ARTIFACT_DESTINATION_FILE ?= ./tmp/idp.tar.gz
lint_country_dialing_codes \
lint_erb \
lint_lockfiles \
lint_new_typescript_files \
lint_optimized_assets \
lint_tracker_events \
lint_yaml \
Expand Down Expand Up @@ -88,6 +89,8 @@ endif
make lint_yaml
@echo "--- lint Yarn workspaces ---"
make lint_yarn_workspaces
@echo "--- lint new TypeScript files ---"
make lint_new_typescript_files
@echo "--- lint lockfiles ---"
make lint_lockfiles
@echo "--- check assets are optimized ---"
Expand Down Expand Up @@ -133,6 +136,9 @@ lint_yarn_lock: package.json yarn.lock ## Lints the package.json and its lockfil

lint_lockfiles: lint_gemfile_lock lint_yarn_lock ## Lints to ensure lockfiles are in sync

lint_new_typescript_files:
scripts/enforce-typescript-files.mjs
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the irony of checking for new typescript files with a plain JS file! 😂


lint_readme: README.md ## Lints README.md
(! git diff --name-only | grep "^README.md$$") || (echo "Error: Run 'make README.md' to regenerate the README.md"; exit 1)

Expand Down
4 changes: 4 additions & 0 deletions app/javascript/packages/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"name": "@18f/identity-assets",
"private": true,
"version": "1.0.0",
"exports": {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some packages are intentionally written in JavaScript because we expect them to be loaded directly by Node.js without any pre/post-processing (e.g. Webpack plugins, Sass compilation, various configurations, etc.). The way I approached this was to check for a defined entrypoint, since this would describe how Node.js would be expected to resolve a package.

".": "./index.ts",
"./webpack-plugin": "./webpack-plugin.js"
},
"peerDependencies": {
"webpack": ">=5"
},
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/packages/build-sass/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
"get-default-load-paths.js",
"get-error-sass-stack-paths.js"
],
"exports": {
".": "./index.js"
},
"license": "CC0-1.0",
"bugs": {
"url": "https://github.com/18f/identity-idp/issues"
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/packages/eslint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "2.0.0",
"private": false,
"description": "ESLint plugin and shareable configurations for Login.gov JavaScript standards",
"exports": {
".": "./index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/18f/identity-idp.git",
Expand Down
3 changes: 3 additions & 0 deletions app/javascript/packages/stylelint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "4.0.0",
"private": false,
"description": "Stylelint shareable configuration for Login.gov CSS/SASS standards",
"exports": {
".": "./index.js"
},
"repository": {
"type": "git",
"url": "https://github.com/18f/identity-idp.git",
Expand Down
97 changes: 97 additions & 0 deletions scripts/enforce-typescript-files.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env node

import assert from 'node:assert';
import { readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import glob from 'fast-glob';

// Do not add to this list! All new scripts should be written in TypeScript, so this list should
// only ever shrink over time. Scripts which are loaded directly by Node.js should exist within
// packages with a defined entrypoint.
const LEGACY_FILE_EXCEPTIONS = [
'app/javascript/packages/compose-components/index.js',
'app/javascript/packages/compose-components/index.spec.jsx',
'app/javascript/packages/device/index.js',
'app/javascript/packages/document-capture/index.js',
'app/javascript/packages/document-capture/components/acuant-capture-canvas.jsx',
'app/javascript/packages/document-capture/components/acuant-selfie-capture-canvas.jsx',
'app/javascript/packages/document-capture/components/callback-on-mount.jsx',
'app/javascript/packages/document-capture/components/file-image.jsx',
'app/javascript/packages/document-capture/components/submission-interstitial.jsx',
'app/javascript/packages/document-capture/components/submission.jsx',
'app/javascript/packages/document-capture/context/device.js',
'app/javascript/packages/document-capture/higher-order/with-props.jsx',
'app/javascript/packages/document-capture/hooks/use-async.js',
'app/javascript/packages/document-capture/hooks/use-cookie.js',
'app/javascript/packages/document-capture/hooks/use-counter.js',
'app/javascript/packages/document-capture/hooks/use-previous.js',
'app/javascript/packages/masked-text-toggle/index.js',
'spec/javascript/packs/form-steps-wait-spec.js',
'spec/javascript/packs/state-guidance-spec.js',
'spec/javascript/packs/webauthn-setup-spec.js',
'spec/javascript/support/console.js',
'spec/javascript/support/document-capture.jsx',
'spec/javascript/support/dom.js',
'spec/javascript/support/file.js',
'spec/javascript/support/mocha.js',
'spec/javascript/packages/device/index-spec.js',
'spec/javascript/packages/document-capture-polling/index-spec.js',
'spec/javascript/packages/masked-text-toggle/index-spec.js',
'spec/javascript/packages/document-capture/components/acuant-camera-spec.jsx',
'spec/javascript/packages/document-capture/components/acuant-capture-canvas-spec.jsx',
'spec/javascript/packages/document-capture/components/acuant-capture-spec.jsx',
'spec/javascript/packages/document-capture/components/acuant-sdk-spec.js',
'spec/javascript/packages/document-capture/components/acuant-selfie-camera-spec.jsx',
'spec/javascript/packages/document-capture/components/acuant-selfie-capture-canvas-spec.jsx',
'spec/javascript/packages/document-capture/components/callback-on-mount-spec.jsx',
'spec/javascript/packages/document-capture/components/document-capture-review-issues-spec.jsx',
'spec/javascript/packages/document-capture/components/document-capture-spec.jsx',
'spec/javascript/packages/document-capture/components/document-capture-warning-spec.jsx',
'spec/javascript/packages/document-capture/components/documents-step-spec.jsx',
'spec/javascript/packages/document-capture/components/file-image-spec.jsx',
'spec/javascript/packages/document-capture/components/file-input-spec.jsx',
'spec/javascript/packages/document-capture/components/review-issues-step-spec.jsx',
'spec/javascript/packages/document-capture/components/submission-complete-spec.jsx',
'spec/javascript/packages/document-capture/components/submission-interstitial-spec.jsx',
'spec/javascript/packages/document-capture/components/submission-spec.jsx',
'spec/javascript/packages/document-capture/components/suspense-error-boundary-spec.jsx',
'spec/javascript/packages/document-capture/components/tip-list-spec.jsx',
'spec/javascript/packages/document-capture/components/unknown-error-spec.jsx',
'spec/javascript/packages/document-capture/components/warning-spec.jsx',
'spec/javascript/packages/document-capture/context/acuant-spec.jsx',
'spec/javascript/packages/document-capture/context/device-spec.jsx',
'spec/javascript/packages/document-capture/context/failed-capture-attempts-spec.jsx',
'spec/javascript/packages/document-capture/context/feature-flag-spec.jsx',
'spec/javascript/packages/document-capture/context/file-base64-cache-spec.js',
'spec/javascript/packages/document-capture/context/index-spec.js',
'spec/javascript/packages/document-capture/context/selfie-capture-spec.jsx',
'spec/javascript/packages/document-capture/context/service-provider-spec.jsx',
'spec/javascript/packages/document-capture/context/upload-spec.jsx',
'spec/javascript/packages/document-capture/higher-order/with-props-spec.jsx',
'spec/javascript/packages/document-capture/hooks/use-async-spec.jsx',
'spec/javascript/packages/document-capture/hooks/use-cookie-spec.jsx',
'spec/javascript/packages/document-capture/hooks/use-counter-spec.jsx',
'spec/javascript/packages/document-capture/hooks/use-previous.spec.js',
'spec/javascript/packages/document-capture/services/upload-spec.js',
];

const packagesWithEntrypoints = await glob('app/javascript/packages/*/package.json')
.then((files) => Promise.all(files.map(async (file) => [file, await readFile(file, 'utf-8')])))
.then((contents) => contents.map(([file, content]) => [file, JSON.parse(content)]))
.then((manifests) => manifests.filter(([_file, manifest]) => manifest.exports || manifest.main))
.then((manifests) => manifests.map(([file]) => dirname(file)));

const jsFiles = await glob(
['app/{javascript/packages,components}/**/*.{js,jsx}', 'spec/javascript/*/**/*.{js,jsx}'],
{
ignore: [...packagesWithEntrypoints.map((path) => join(path, '**')), ...LEGACY_FILE_EXCEPTIONS],
},
);

assert(
!jsFiles.length,
`All new JavaScript files should be written with TypeScript extensions (.ts, .tsx).

Found ${JSON.stringify(jsFiles)}
`,
);