-
Notifications
You must be signed in to change notification settings - Fork 2
CLOUDP-352308 Developer experience improvements and webpack HMR support #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nima-taheri-mongodb
wants to merge
11
commits into
mongodb-labs:master
Choose a base branch
from
nima-taheri-mongodb:some-improvements
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
a413635
conf: ignore vscode & idea IDE files
nima-taheri-mongodb fbc1d4d
conf: ensure nodejs 24.x
nima-taheri-mongodb 966dc28
conf: add package.json for type-hints and prettier
nima-taheri-mongodb ea4066d
conf: basic prettier config
nima-taheri-mongodb c37b4f6
feat: add a regex util to be able to write webpack-style patterns insβ¦
nima-taheri-mongodb 784551e
refactor: move rules to a separate file with tests
nima-taheri-mongodb dde11da
feat: add redirect rule for webpack hot-updates
nima-taheri-mongodb c1336eb
feat: include "other" to js redirects
nima-taheri-mongodb e0b6798
feat: add mongo-cdn presets to options dialog
nima-taheri-mongodb b39ad05
processed review feedbacks
nima-taheri-mongodb da4ad9e
docs: remove the old comment
nima-taheri-mongodb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "printWidth": 120, | ||
| "tabWidth": 2, | ||
| "useTabs": false, | ||
| "semi": true, | ||
| "singleQuote": true, | ||
| "trailingComma": "es5", | ||
| "bracketSpacing": true, | ||
| "arrowParens": "always" | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| nodejs 24.10.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,54 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>redwood</title> | ||
| <style> | ||
| body { padding: 10px; } | ||
| label { display: block; margin-bottom: 5px; } | ||
| label > span { display: inline-block; font-weight: bold; min-width: 120px; padding-right: 10px; text-align: right; } | ||
| label > input { width: 210px; } | ||
| footer { margin-top: 20px; text-align: center; } | ||
| button { cursor: pointer; } | ||
| </style> | ||
| <script src="options.js"></script> | ||
| <title>redwood</title> | ||
| <style> | ||
| body { | ||
| padding: 10px; | ||
| } | ||
|
|
||
| label { | ||
| display: block; | ||
| } | ||
|
|
||
| label > span { | ||
| display: inline-block; | ||
| font-weight: bold; | ||
| min-width: 120px; | ||
| padding-right: 10px; | ||
| } | ||
|
|
||
| label > input[type='text'] { | ||
| width: 400px; | ||
| } | ||
|
|
||
| footer { | ||
| margin-top: 20px; | ||
| text-align: center; | ||
| } | ||
|
|
||
| button { | ||
| cursor: pointer; | ||
| } | ||
| </style> | ||
| <script src="options.js"></script> | ||
| </head> | ||
| <body> | ||
| <main> | ||
| <label><span>Match URL prefix</span><input name="match"></label> | ||
| <label><span>Replace URL prefix</span><input name="replace"></label> | ||
| <label><span>Webpack Dev Server?</span><input name="wds"></label> | ||
| <label><span>Web Server Port (if using wds)</span><input name="webServerPort"></label> | ||
| <label><span>Compressed?</span><input name="compressed"></label> | ||
| <footer> | ||
| <button name="save">Save</button> | ||
| </footer> | ||
| </main> | ||
| <main> | ||
| <div> | ||
| <span>Redirect MongoCDN assets from:</span> | ||
| <label><input type="checkbox" name="redirect-mongo-cdn-development" value="Development"/> Development</label> | ||
| <label><input type="checkbox" name="redirect-mongo-cdn-qa" value="QA"/> QA</label> | ||
| <label><input type="checkbox" name="redirect-mongo-cdn-production" value="Production"/> Production</label> | ||
| </div> | ||
| <br/> | ||
| <label> | ||
| <span>To local assets URL:</span> | ||
| <input type="text" name="local-assets-url" id="local-assets-url"> | ||
| </label> | ||
| <footer> | ||
| <button type="button" name="save">Save</button> | ||
| </footer> | ||
| </main> | ||
| </body> | ||
| </html> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,37 @@ | ||
| setInputs = (items) => { | ||
| document.querySelector('input[name=redirect-mongo-cdn-development]').checked = items.redirectMongoCdnDevelopment; | ||
| document.querySelector('input[name=redirect-mongo-cdn-qa]').checked = items.redirectMongoCdnQa; | ||
| document.querySelector('input[name=redirect-mongo-cdn-production]').checked = items.redirectMongoCdnProduction; | ||
| document.querySelector('input[name=local-assets-url]').value = items.localAssetsUrl; | ||
| }; | ||
|
|
||
| document.addEventListener('DOMContentLoaded', () => { | ||
| // Use default value color = 'red' and likesColor = true. | ||
| chrome.storage.sync.get({ | ||
| match: 'https://example.com', | ||
| replace: 'http://127.0.0.1:80', | ||
| wds: true, | ||
| webServerPort: 8080, | ||
| compressed: false | ||
| }, items => { | ||
| document.querySelector('input[name=match]').value = items.match | ||
| document.querySelector('input[name=replace]').value = items.replace | ||
| document.querySelector('input[name=wds]').value = items.wds | ||
| document.querySelector('input[name=webServerPort]').value = items.webServerPort | ||
| document.querySelector('input[name=compressed]').value = items.compressed | ||
| }) | ||
| // Use default value color = 'red' and likesColor = true. | ||
| chrome.storage.sync.get( | ||
| { | ||
| redirectMongoCdnDevelopment: true, | ||
| redirectMongoCdnQa: false, | ||
| redirectMongoCdnProduction: false, | ||
| localAssetsUrl: 'http://localhost:8081', | ||
| }, | ||
| (items) => { | ||
| setInputs(items); | ||
| } | ||
| ); | ||
|
|
||
| const getInput = name => document.querySelector(`input[name=${name}]`).value | ||
| const getInput = (name) => document.querySelector(`input[name=${name}]`).value; | ||
| const getCheckbox = (name) => document.querySelector(`input[name=${name}]`).checked; | ||
|
|
||
| document.querySelector('button[name=save]').addEventListener('click', () => { | ||
| chrome.storage.sync.set({ | ||
| match: getInput('match'), | ||
| replace: getInput('replace'), | ||
| wds: getInput('wds'), | ||
| webServerPort: getInput('webServerPort'), | ||
| compressed: getInput('compressed') | ||
| }, chrome.runtime.reload) | ||
| }) | ||
| }) | ||
| document.querySelector('button[name=save]').addEventListener('click', () => { | ||
| chrome.storage.sync.set( | ||
| { | ||
| redirectMongoCdnDevelopment: getCheckbox('redirect-mongo-cdn-development'), | ||
| redirectMongoCdnQa: getCheckbox('redirect-mongo-cdn-qa'), | ||
| redirectMongoCdnProduction: getCheckbox('redirect-mongo-cdn-production'), | ||
| localAssetsUrl: getInput('local-assets-url'), | ||
| }, | ||
| chrome.runtime.reload | ||
| ); | ||
| window.close(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "name": "redwood", | ||
| "version": "1.0.0", | ||
| "description": "Redirect network requests for assets to test production services with local changes.", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "test": "node --test", | ||
| "test:watch": "node --test --watch" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/chrome": "^0.0.270", | ||
| "@types/node": "^24.8.1", | ||
| "prettier": "^3.6.2" | ||
| } | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| /** | ||
| * A registry for named capture groups that tracks their numeric indices. | ||
| */ | ||
| class NamedGroupRegistry { | ||
| constructor() { | ||
| this.nameToNumber = new Map(); | ||
| } | ||
|
|
||
| /** | ||
| * Registers a named capture group with its position number. | ||
| * @param {string} name - The name of the capture group. | ||
| * @param {number} position - The position/number of this capture group. | ||
| * @throws {Error} If the named group is already registered. | ||
| */ | ||
| registerNamedGroup(name, position) { | ||
| if (this.nameToNumber.has(name)) { | ||
| throw new Error(`Named capture group '${name}' is already defined!`); | ||
| } | ||
| this.nameToNumber.set(name, position); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the numeric index for a named capture group. | ||
| * @param {string} name - The name of the capture group. | ||
| * @returns {number} The numeric index. | ||
| * @throws {Error} If the named group is not registered. | ||
| */ | ||
| getGroupNumber(name) { | ||
| if (!this.nameToNumber.has(name)) { | ||
| throw new Error(`Named capture group '${name}' is not defined in the search pattern!`); | ||
| } | ||
| return this.nameToNumber.get(name); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Converts named capture groups to numbered capture groups. | ||
| * | ||
| * This function takes a regex pattern with .NET-style named capture groups ((?<name>...)) | ||
| * and a replacement pattern with JavaScript-style named references (${name}), and converts | ||
| * them to numbered capture groups and backreferences. | ||
| * | ||
| * @param {string} searchRegex - The regex pattern with (?<name>...) syntax. | ||
| * @param {string} replaceRegex - The replacement pattern with ${name} syntax. | ||
| * @param {Object} [options={}] - Configuration options. | ||
| * @param {string} [options.backReferenceSymbol='\\'] - Symbol for backreferences ('\\' or '$'). | ||
| * | ||
| * @returns {Object} An object containing: | ||
| * - searchRegex {string}: The regex pattern with numbered groups. | ||
| * - replaceRegex {string}: The replacement pattern with numbered backreferences. | ||
| * | ||
| * @throws {Error} If duplicate named groups are found or undefined groups are referenced. | ||
| * | ||
| * @example | ||
| * const result = simplifyNamedGroups( | ||
| * '(?<protocol>https?)://(?<domain>[^/]+)/(?<path>.*)', | ||
| * '${protocol}://localhost/${path}', | ||
| * { backReferenceSymbol: '\\' } | ||
| * ); | ||
| * // Result: | ||
| * // searchRegex: '(https?)://([^/]+)/(.*)' | ||
| * // replaceRegex: '\\1://localhost/\\3' | ||
| */ | ||
| export function simplifyNamedGroups(searchRegex, replaceRegex, options = {}) { | ||
| options.backReferenceSymbol = options.backReferenceSymbol || '\\'; | ||
|
|
||
| const registry = new NamedGroupRegistry(); | ||
|
|
||
| // Step 1: Scan the entire pattern to count ALL capture groups (both named and regular) | ||
| // and register named groups with their correct positions | ||
| let groupCounter = 0; | ||
| let pos = 0; | ||
|
|
||
| while (pos < searchRegex.length) { | ||
| const char = searchRegex[pos]; | ||
|
|
||
| // Skip escaped characters | ||
| if (char === '\\') { | ||
| pos += 2; | ||
| continue; | ||
| } | ||
|
|
||
| // Check if this is a capture group | ||
| if (char === '(') { | ||
| // Check if it's a named group (?<name>...) | ||
| if (searchRegex[pos + 1] === '?' && searchRegex[pos + 2] === '<') { | ||
| // Extract the name | ||
| const nameMatch = searchRegex.slice(pos).match(/^\(\?<([^>]+)>/); | ||
| if (nameMatch) { | ||
| groupCounter++; | ||
| registry.registerNamedGroup(nameMatch[1], groupCounter); | ||
| pos += nameMatch[0].length; | ||
| continue; | ||
| } | ||
| } | ||
| // Check if it's a non-capturing group (?:...) | ||
| else if (searchRegex[pos + 1] === '?' && searchRegex[pos + 2] === ':') { | ||
| pos++; | ||
| continue; | ||
| } | ||
| // Check for other non-capturing syntax like (?=...), (?!...), etc. | ||
| else if (searchRegex[pos + 1] === '?') { | ||
| pos++; | ||
| continue; | ||
| } | ||
| // It's a regular capturing group | ||
| else { | ||
| groupCounter++; | ||
| pos++; | ||
| continue; | ||
| } | ||
| } | ||
|
|
||
| pos++; | ||
| } | ||
|
|
||
| // Step 2: Convert search pattern - replace (?<name>...) with (...) | ||
| let simplifiedSearch = searchRegex; | ||
| let match; | ||
| let offset = 0; | ||
|
|
||
| // Match named groups | ||
| const namedGroupStart = /\(\?<([^>]+)>/g; | ||
|
|
||
| while ((match = namedGroupStart.exec(searchRegex)) !== null) { | ||
| const groupName = match[1]; | ||
| const startPos = match.index; | ||
| const contentStart = match.index + match[0].length; | ||
|
|
||
| // Find the matching closing parenthesis | ||
| let depth = 1; | ||
| let endPos = contentStart; | ||
|
|
||
| while (depth > 0 && endPos < searchRegex.length) { | ||
| const char = searchRegex[endPos]; | ||
|
|
||
| if (char === '\\') { | ||
| // Skip escaped characters | ||
| endPos += 2; | ||
| continue; | ||
| } | ||
|
|
||
| if (char === '(') { | ||
| depth++; | ||
| } else if (char === ')') { | ||
| depth--; | ||
| } | ||
|
|
||
| endPos++; | ||
| } | ||
|
|
||
| if (depth !== 0) { | ||
| throw new Error(`Unmatched parentheses for named group '${groupName}'`); | ||
| } | ||
|
|
||
| // Replace (?<name>...) with (...) | ||
| const groupContent = searchRegex.slice(contentStart, endPos - 1); | ||
| const replacement = `(${groupContent})`; | ||
|
|
||
| simplifiedSearch = | ||
| simplifiedSearch.slice(0, startPos + offset) + | ||
| replacement + | ||
| simplifiedSearch.slice(startPos + offset + (endPos - startPos)); | ||
|
|
||
| // Adjust offset for the length difference | ||
| offset += replacement.length - (endPos - startPos); | ||
| } | ||
|
|
||
| // Step 3: Parse and convert replacement pattern | ||
| // Match ${name} references | ||
| const namedRefRegex = /\$\{([^}]+)\}/g; | ||
|
|
||
| const simplifiedReplace = replaceRegex.replace(namedRefRegex, (fullMatch, name) => { | ||
| const groupNumber = registry.getGroupNumber(name); | ||
| return `${options.backReferenceSymbol}${groupNumber}`; | ||
| }); | ||
|
|
||
| return { | ||
| searchRegex: simplifiedSearch, | ||
| replaceRegex: simplifiedReplace, | ||
| }; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In retrospect, it's ridiculous that we left this as a text input for as long as we did