From b26051a38f3ee0144ad30b3dc7063295c5a9112c Mon Sep 17 00:00:00 2001 From: Victor Bojica Date: Tue, 15 Oct 2024 17:37:37 +0300 Subject: [PATCH 1/4] added attack protection suite frontend SDK import fixes and updated backend snippets to correctly use the api and minor cleanup --- v2/attackprotectionsuite/backend-setup.mdx | 120 +- v2/attackprotectionsuite/frontend-setup.mdx | 40 +- v2/src/plugins/codeTypeChecking/index.js | 1506 +++++++++++-------- 3 files changed, 1021 insertions(+), 645 deletions(-) diff --git a/v2/attackprotectionsuite/backend-setup.mdx b/v2/attackprotectionsuite/backend-setup.mdx index 563016a2a..a27e18af1 100644 --- a/v2/attackprotectionsuite/backend-setup.mdx +++ b/v2/attackprotectionsuite/backend-setup.mdx @@ -401,6 +401,7 @@ async function handleSecurityChecks(input: { }); } catch (err) { // silently fail in order to not break the auth flow + console.error(err); return; } @@ -505,10 +506,11 @@ SuperTokens.init({ const actionType = 'emailpassword-sign-up'; const ip = getIpFromRequest(input.options.req.original); let email = input.formFields.filter((f) => f.id === "email")[0].value; + let password = input.formFields.filter((f) => f.id === "password")[0].value; const bruteForceConfig = getBruteForceConfig(email, ip, actionType); // we check the anomaly detection service before calling the original implementation of signUp - let securityCheckResponse = await handleSecurityChecks({ ...input, requestId, email, bruteForceConfig, actionType }); + let securityCheckResponse = await handleSecurityChecks({ requestId, email, bruteForceConfig, actionType }); if(securityCheckResponse !== undefined) { return securityCheckResponse; } @@ -531,7 +533,7 @@ SuperTokens.init({ const bruteForceConfig = getBruteForceConfig(email, ip, actionType); // we check the anomaly detection service before calling the original implementation of signIn - let securityCheckResponse = await handleSecurityChecks({ ...input, requestId, email, bruteForceConfig, actionType }); + let securityCheckResponse = await handleSecurityChecks({ requestId, email, bruteForceConfig, actionType }); if(securityCheckResponse !== undefined) { return securityCheckResponse; } @@ -554,12 +556,20 @@ SuperTokens.init({ const bruteForceConfig = getBruteForceConfig(email, ip, actionType); // we check the anomaly detection service before calling the original implementation of generatePasswordResetToken - let securityCheckResponse = await handleSecurityChecks({ ...input, requestId, email, bruteForceConfig, actionType }); + let securityCheckResponse = await handleSecurityChecks({ requestId, email, bruteForceConfig, actionType }); if(securityCheckResponse !== undefined) { return securityCheckResponse; } return originalImplementation.generatePasswordResetTokenPOST!(input); + }, + passwordResetPOST: async function (input) { + let password = input.formFields.filter((f) => f.id === "password")[0].value; + let securityCheckResponse = await handleSecurityChecks({ password }); + if (securityCheckResponse !== undefined) { + return securityCheckResponse; + } + return originalImplementation.passwordResetPOST!(input); } } } @@ -888,7 +898,7 @@ func main() { return resp, nil } - // rewrite the original implementation of SignInPOST + // rewrite the original implementation of GeneratePasswordResetTokenPOST originalGeneratePasswordResetTokenPOST := *originalImplementation.GeneratePasswordResetTokenPOST (*originalImplementation.GeneratePasswordResetTokenPOST) = func(formFields []epmodels.TypeFormField, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.GeneratePasswordResetTokenPOSTResponse, error) { // Generate request ID for bot and suspicious IP detection @@ -949,6 +959,46 @@ func main() { return resp, nil } + // rewrite the original implementation of PasswordResetPOST + originalPasswordResetPOST := *originalImplementation.PasswordResetPOST + (*originalImplementation.PasswordResetPOST) = func(formFields []epmodels.TypeFormField, token string, tenantId string, options epmodels.APIOptions, userContext supertokens.UserContext) (epmodels.ResetPasswordPOSTResponse, error) { + password := "" + for _, field := range formFields { + if field.ID == "password" { + valueAsString, asStrOk := field.Value.(string) + if !asStrOk { + return epmodels.ResetPasswordPOSTResponse{}, errors.New("Should never come here as we check the type during validation") + } + password = valueAsString + } + } + + // Check anomaly detection service before proceeding + checkErr, err := handleSecurityChecks( + SecurityCheckInput{ + Password: password, + }, + ) + if err != nil { + return epmodels.ResetPasswordPOSTResponse{}, err + } + + if checkErr != nil { + return epmodels.ResetPasswordPOSTResponse{ + GeneralError: checkErr, + }, nil + } + + // First we call the original implementation + resp, err := originalPasswordResetPOST(formFields, token, tenantId, options, userContext) + + if err != nil { + return epmodels.ResetPasswordPOSTResponse{}, err + } + + return resp, nil + } + return originalImplementation }, Functions: func(originalImplementation epmodels.RecipeInterface) epmodels.RecipeInterface { @@ -993,7 +1043,7 @@ SECRET_API_KEY = ""; # Your secret API key that you received fro # The full URL with the correct region will be provided by the SuperTokens team ANOMALY_DETECTION_API_URL = "https://security-.aws.supertokens.io/v1/security" -async def handle_security_checks(request_id: Union[str, None], password: Union[str, None], brute_force_config: List[Dict[str, Any]], email: Union[str, None], phone_number: Union[str, None], action_type: str) -> Union[GeneralErrorResponse, None]: +async def handle_security_checks(request_id: Union[str, None], password: Union[str, None], brute_force_config: Union[List[Dict[str, Any]], None], email: Union[str, None], phone_number: Union[str, None], action_type: Union[str, None]) -> Union[GeneralErrorResponse, None]: request_body = {} if request_id is not None: @@ -1123,7 +1173,7 @@ def override_email_password_apis(original_implementation: APIInterface): email = field.value brute_force_config = get_brute_force_config(email, ip, action_type) - # we check the anomaly detection service before calling the original implementation of signUp + # we check the anomaly detection service before calling the original implementation of sign_in_post security_check_response = await handle_security_checks( request_id=request_id, password=None, @@ -1135,7 +1185,7 @@ def override_email_password_apis(original_implementation: APIInterface): if security_check_response is not None: return security_check_response - # We need to call the original implementation of sign_up_post. + # We need to call the original implementation of sign_in_post. response = await original_sign_in_post(form_fields, tenant_id, api_options, user_context) return response @@ -1159,7 +1209,7 @@ def override_email_password_apis(original_implementation: APIInterface): email = field.value brute_force_config = get_brute_force_config(email, ip, action_type) - # we check the anomaly detection service before calling the original implementation of signUp + # we check the anomaly detection service before calling the original implementation of generate_password_reset_token_post security_check_response = await handle_security_checks( request_id=request_id, password=None, @@ -1171,12 +1221,45 @@ def override_email_password_apis(original_implementation: APIInterface): if security_check_response is not None: return security_check_response - # We need to call the original implementation of sign_up_post. + # We need to call the original implementation of generate_password_reset_token_post. response = await original_generate_password_reset_token_post(form_fields, tenant_id, api_options, user_context) return response original_implementation.generate_password_reset_token_post = generate_password_reset_token_post + + original_password_reset_post = original_implementation.password_reset_post + async def password_reset_post( + form_fields: List[FormField], + token: str, + tenant_id: str, + api_options: APIOptions, + user_context: Dict[str, Any], + ): + password = None + for field in form_fields: + if field.id == "password": + password = field.value + + # we check the anomaly detection service before calling the original implementation of password_reset_post + security_check_response = await handle_security_checks( + request_id=None, + password=password, + brute_force_config=None, + email=None, + phone_number=None, + action_type=None + ) + if security_check_response is not None: + return security_check_response + + response = await original_password_reset_post( + form_fields, token, tenant_id, api_options, user_context + ) + + return response + original_implementation.password_reset_post = password_reset_post + return original_implementation # highlight-end @@ -1276,6 +1359,7 @@ async function handleSecurityChecks(input: { }); } catch (err) { // silently fail in order to not break the auth flow + console.error(err); return; } let responseData = response.data; @@ -1336,8 +1420,8 @@ SuperTokens.init({ const emailOrPhoneNumber = "email" in input ? input.email : input.phoneNumber; const bruteForceConfig = getBruteForceConfig(emailOrPhoneNumber, ip, actionType); - // we check the anomaly detection service before calling the original implementation of signUp - let securityCheckResponse = await handleSecurityChecks({ ...input, bruteForceConfig, actionType }); + // we check the anomaly detection service before calling the original implementation of createCodePOST + let securityCheckResponse = await handleSecurityChecks({ bruteForceConfig, actionType }); if(securityCheckResponse !== undefined) { return securityCheckResponse; } @@ -1357,8 +1441,8 @@ SuperTokens.init({ const bruteForceConfig = getBruteForceConfig(userIdentifier, ip, actionType); - // we check the anomaly detection service before calling the original implementation of signUp - let securityCheckResponse = await handleSecurityChecks({ ...input, phoneNumber, email, bruteForceConfig, actionType }); + // we check the anomaly detection service before calling the original implementation of resendCodePOST + let securityCheckResponse = await handleSecurityChecks({ phoneNumber, email, bruteForceConfig, actionType }); if(securityCheckResponse !== undefined) { return securityCheckResponse; } @@ -1631,7 +1715,7 @@ SECRET_API_KEY = "" # Your secret API key that you received from # The full URL with the correct region will be provided by the SuperTokens team ANOMALY_DETECTION_API_URL = "https://security-.aws.supertokens.io/v1/security" -async def handle_security_checks(request_id: Union[str, None], password: Union[str, None], brute_force_config: List[Dict[str, Any]], email: Union[str, None], phone_number: Union[str, None], action_type: str) -> Union[GeneralErrorResponse, None]: +async def handle_security_checks(request_id: Union[str, None], password: Union[str, None], brute_force_config: Union[List[Dict[str, Any]], None], email: Union[str, None], phone_number: Union[str, None], action_type: Union[str, None]) -> Union[GeneralErrorResponse, None]: request_body = {} request_body['bruteForce'] = brute_force_config @@ -1693,7 +1777,7 @@ def override_passwordless_apis(original_implementation: APIInterface): identifier = phone_number brute_force_config = get_brute_force_config(identifier, ip, action_type) - # we check the anomaly detection service before calling the original implementation of signUp + # we check the anomaly detection service before calling the original implementation of create_code_post security_check_response = await handle_security_checks( request_id=None, password=None, @@ -1705,7 +1789,7 @@ def override_passwordless_apis(original_implementation: APIInterface): if security_check_response is not None: return security_check_response - # We need to call the original implementation of sign_up_post. + # We need to call the original implementation of create_code_post. response = await original_create_code_post(email, phone_number, tenant_id, api_options, user_context) return response @@ -1728,7 +1812,7 @@ def override_passwordless_apis(original_implementation: APIInterface): identifier = phone_number brute_force_config = get_brute_force_config(identifier, ip, action_type) - # we check the anomaly detection service before calling the original implementation of signUp + # we check the anomaly detection service before calling the original implementation of resend_code_post security_check_response = await handle_security_checks( request_id=None, password=None, @@ -1740,7 +1824,7 @@ def override_passwordless_apis(original_implementation: APIInterface): if security_check_response is not None: return security_check_response - # We need to call the original implementation of sign_up_post. + # We need to call the original implementation of resend_code_post. response = await original_resend_code_post(device_id, pre_auth_session_id, tenant_id, api_options, user_context) return response diff --git a/v2/attackprotectionsuite/frontend-setup.mdx b/v2/attackprotectionsuite/frontend-setup.mdx index cc5bd5e94..285c98de5 100644 --- a/v2/attackprotectionsuite/frontend-setup.mdx +++ b/v2/attackprotectionsuite/frontend-setup.mdx @@ -20,17 +20,15 @@ Below is an example of how to implement request ID generation on your frontend: ```tsx -const PUBLIC_API_KEY = ""; // Your public API key that you received from the SuperTokens team -const SDK_URL = "https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s"; -const PROXY_ENDPOINT_URL = "https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/CnsdzKsyFKU8Q3h2" const ENVIRONMENT_ID = ""; // Your environment ID that you received from the SuperTokens team -// Initialize the agent on page load. -const supertokensRequestIdPromise = import(SDK_URL + "?apiKey=" + PUBLIC_API_KEY).then((RequestId: any) => RequestId.load({ - endpoint: [ - PROXY_ENDPOINT_URL, - RequestId.defaultEndpoint - ] -})); +// Initialize the agent on page load using your public API key that you received from the SuperTokens team. +const supertokensRequestIdPromise = require("https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s?apiKey=") + .then((RequestId: any) => RequestId.load({ + endpoint: [ + 'https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/CnsdzKsyFKU8Q3h2', + RequestId.defaultEndpoint + ] + })); async function getRequestId() { const sdk = await supertokensRequestIdPromise; @@ -43,6 +41,10 @@ async function getRequestId() { } ``` +:::note +If you are using a build system such as Webpack, make sure that you do not use string concatenation or interpolation when importing the SDK. This is because the SDK URL is a dynamic URL and the build system might not be able to resolve it during static analysis. +::: + ### Passing the Request ID to the Backend Once you have generated the request ID on the frontend, you need to pass it to the backend. This is done by including the `requestId` property along with the value as part of the preAPIHook body from the initialisation of the recipes. @@ -55,17 +57,15 @@ Below is a full example of how to configure the SDK and pass the request ID to t ```tsx import EmailPassword from "supertokens-auth-react/recipe/emailpassword"; -const PUBLIC_API_KEY = ""; // Your public API key that you received from the SuperTokens team -const SDK_URL = "https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s"; -const PROXY_ENDPOINT_URL = "https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/CnsdzKsyFKU8Q3h2" const ENVIRONMENT_ID = ""; // Your environment ID that you received from the SuperTokens team -// Initialize the agent on page load. -const supertokensRequestIdPromise = import(SDK_URL + "?apiKey=" + PUBLIC_API_KEY).then((RequestId: any) => RequestId.load({ - endpoint: [ - PROXY_ENDPOINT_URL, - RequestId.defaultEndpoint - ] -})); +// Initialize the agent on page load using your public API key that you received from the SuperTokens team. +const supertokensRequestIdPromise = require("https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s?apiKey=") + .then((RequestId: any) => RequestId.load({ + endpoint: [ + 'https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/CnsdzKsyFKU8Q3h2', + RequestId.defaultEndpoint + ] + })); async function getRequestId() { const sdk = await supertokensRequestIdPromise; diff --git a/v2/src/plugins/codeTypeChecking/index.js b/v2/src/plugins/codeTypeChecking/index.js index b4201749c..41946c08e 100644 --- a/v2/src/plugins/codeTypeChecking/index.js +++ b/v2/src/plugins/codeTypeChecking/index.js @@ -1,10 +1,10 @@ -let fs = require('fs'); -let readdir = require("fs").promises.readdir -let path = require('path'); -var exec = require('child_process').exec; -var crypto = require('crypto'); +let fs = require("fs"); +let readdir = require("fs").promises.readdir; +let path = require("path"); +var exec = require("child_process").exec; +var crypto = require("crypto"); let mdVars = require("../markdownVariables.json"); -const { execSync } = require('child_process'); +const { execSync } = require("child_process"); let defaultMainContent = ` @@ -16,685 +16,977 @@ void main() { let mainContent = defaultMainContent; function hash(input) { - return crypto.createHash('md5').update(input).digest('hex'); + return crypto.createHash("md5").update(input).digest("hex"); } async function addCodeSnippetToEnv(mdFile, isSwiftEnabled) { - if (mdFile.includes("/v2/change_me/") || mdFile.includes("/v2/contribute/") || - mdFile.includes("/v2/nodejs") || mdFile.includes("/v2/golang") || mdFile.includes("/v2/python") || - mdFile.includes("/v2/auth-react") || mdFile.includes("/v2/website") || mdFile.includes("/v2/react-native") || mdFile.includes("/codeTypeChecking/")) { - return; - } - return new Promise((res, rej) => { - fs.readFile(mdFile, 'utf8', async (err, data) => { - if (err) { - return rej(err); + if ( + mdFile.includes("/v2/change_me/") || + mdFile.includes("/v2/contribute/") || + mdFile.includes("/v2/nodejs") || + mdFile.includes("/v2/golang") || + mdFile.includes("/v2/python") || + mdFile.includes("/v2/auth-react") || + mdFile.includes("/v2/website") || + mdFile.includes("/v2/react-native") || + mdFile.includes("/codeTypeChecking/") + ) { + return; + } + return new Promise((res, rej) => { + fs.readFile(mdFile, "utf8", async (err, data) => { + if (err) { + return rej(err); + } + let fileNameCounter = 0; + let originalData = data; + + let lines = originalData.split("\n"); + + let currentCodeSnippet = ""; + let currentCodeLanguage = ""; + let startAppendingToCodeSnippet = false; + for (let i = 0; i < lines.length; i++) { + let currLineTrimmed = lines[i].trim(); + if (startAppendingToCodeSnippet && !currLineTrimmed.startsWith("```")) { + currentCodeSnippet = currentCodeSnippet + "\n" + lines[i]; + } + if (currLineTrimmed.startsWith("```")) { + startAppendingToCodeSnippet = !startAppendingToCodeSnippet; + if (!startAppendingToCodeSnippet) { + if (currLineTrimmed !== "```") { + return rej( + new Error( + `Something wrong in how a code snippet has ended in ${mdFile}` + ) + ); } - let fileNameCounter = 0; - let originalData = data; - - let lines = originalData.split("\n"); - - let currentCodeSnippet = ""; - let currentCodeLanguage = ""; - let startAppendingToCodeSnippet = false; - for (let i = 0; i < lines.length; i++) { - let currLineTrimmed = lines[i].trim(); - if (startAppendingToCodeSnippet && !currLineTrimmed.startsWith("```")) { - currentCodeSnippet = currentCodeSnippet + "\n" + lines[i]; - } - if (currLineTrimmed.startsWith("```")) { - startAppendingToCodeSnippet = !startAppendingToCodeSnippet; - if (!startAppendingToCodeSnippet) { - if (currLineTrimmed !== "```") { - return rej(new Error(`Something wrong in how a code snippet has ended in ${mdFile}`)); - } - // we just finished copying a code snippet - if (currentCodeLanguage !== "ignore") { - await addCodeSnippetToEnvHelper(currentCodeSnippet, currentCodeLanguage, mdFile, fileNameCounter); - fileNameCounter++; - } - currentCodeSnippet = ""; - currentCodeLanguage = ""; - } else { - // we are starting a code block - if (currLineTrimmed === "```js" || currLineTrimmed.startsWith("```js ") || - currLineTrimmed === "```jsx" || currLineTrimmed.startsWith("```jsx ")) { - return rej(new Error(`Please do not use js or jsx in code snippets. Only ts or tsx. Error in ` + mdFile)); - } else if (currLineTrimmed === "```ts" || currLineTrimmed.startsWith("```ts ") || - currLineTrimmed === "```tsx" || currLineTrimmed.startsWith("```tsx ")) { - currentCodeLanguage = "typescript"; - } else if (currLineTrimmed === "```go" || currLineTrimmed.startsWith("```go ")) { - currentCodeLanguage = "go"; - } else if (currLineTrimmed === "```python" || currLineTrimmed.startsWith("```python ")) { - currentCodeLanguage = "python"; - } else if (currLineTrimmed === "```kotlin" || currLineTrimmed.startsWith("```kotlin ")) { - currentCodeLanguage = "kotlin"; - } else if (currLineTrimmed === "```swift" || currLineTrimmed.startsWith("```swift ")) { - currentCodeLanguage = isSwiftEnabled ? "swift" : "ignore" - } else if (currLineTrimmed.includes("bash") || currLineTrimmed.includes("yaml") || currLineTrimmed.includes("cql") || currLineTrimmed.includes("sql") || currLineTrimmed.includes("batch") || - currLineTrimmed.includes("text") || currLineTrimmed.includes("json") - || currLineTrimmed.includes("html")) { - currentCodeLanguage = "ignore" - } else if (currLineTrimmed === "```dart" || currLineTrimmed.startsWith("```dart ")) { - currentCodeLanguage = "dart"; - } else { - return rej(new Error(`UNABLE TO RECOGNISE LANGUAGE ${currLineTrimmed} in file ${mdFile}.`)); - } - } - } + // we just finished copying a code snippet + if (currentCodeLanguage !== "ignore") { + await addCodeSnippetToEnvHelper( + currentCodeSnippet, + currentCodeLanguage, + mdFile, + fileNameCounter + ); + fileNameCounter++; } - res(); - }); + currentCodeSnippet = ""; + currentCodeLanguage = ""; + } else { + // we are starting a code block + if ( + currLineTrimmed === "```js" || + currLineTrimmed.startsWith("```js ") || + currLineTrimmed === "```jsx" || + currLineTrimmed.startsWith("```jsx ") + ) { + return rej( + new Error( + `Please do not use js or jsx in code snippets. Only ts or tsx. Error in ` + + mdFile + ) + ); + } else if ( + currLineTrimmed === "```ts" || + currLineTrimmed.startsWith("```ts ") || + currLineTrimmed === "```tsx" || + currLineTrimmed.startsWith("```tsx ") + ) { + currentCodeLanguage = "typescript"; + } else if ( + currLineTrimmed === "```go" || + currLineTrimmed.startsWith("```go ") + ) { + currentCodeLanguage = "go"; + } else if ( + currLineTrimmed === "```python" || + currLineTrimmed.startsWith("```python ") + ) { + currentCodeLanguage = "python"; + } else if ( + currLineTrimmed === "```kotlin" || + currLineTrimmed.startsWith("```kotlin ") + ) { + currentCodeLanguage = "kotlin"; + } else if ( + currLineTrimmed === "```swift" || + currLineTrimmed.startsWith("```swift ") + ) { + currentCodeLanguage = isSwiftEnabled ? "swift" : "ignore"; + } else if ( + currLineTrimmed.includes("bash") || + currLineTrimmed.includes("yaml") || + currLineTrimmed.includes("cql") || + currLineTrimmed.includes("sql") || + currLineTrimmed.includes("batch") || + currLineTrimmed.includes("text") || + currLineTrimmed.includes("json") || + currLineTrimmed.includes("html") + ) { + currentCodeLanguage = "ignore"; + } else if ( + currLineTrimmed === "```dart" || + currLineTrimmed.startsWith("```dart ") + ) { + currentCodeLanguage = "dart"; + } else { + return rej( + new Error( + `UNABLE TO RECOGNISE LANGUAGE ${currLineTrimmed} in file ${mdFile}.` + ) + ); + } + } + } + } + res(); }); + }); } async function getFiles(dir) { - if (!fs.existsSync(dir)) { - return []; - } - const dirents = await readdir(dir, { withFileTypes: true }); - const files = await Promise.all(dirents.map((dirent) => { - const res = path.resolve(dir, dirent.name); - return dirent.isDirectory() ? getFiles(res) : res; - })); - return Array.prototype.concat(...files); + if (!fs.existsSync(dir)) { + return []; + } + const dirents = await readdir(dir, { withFileTypes: true }); + const files = await Promise.all( + dirents.map((dirent) => { + const res = path.resolve(dir, dirent.name); + return dirent.isDirectory() ? getFiles(res) : res; + }) + ); + return Array.prototype.concat(...files); } function cleanEmptyFoldersRecursively(folder) { - if (!fs.existsSync(folder)) { - return; - } - var isDir = fs.statSync(folder).isDirectory(); - if (!isDir) { - return; - } - var files = fs.readdirSync(folder); - if (files.length > 0) { - files.forEach(function (file) { - var fullPath = path.join(folder, file); - cleanEmptyFoldersRecursively(fullPath); - }); - - // re-evaluate files; after deleting subfolder - // we may have parent folder empty now - files = fs.readdirSync(folder); - } + if (!fs.existsSync(folder)) { + return; + } + var isDir = fs.statSync(folder).isDirectory(); + if (!isDir) { + return; + } + var files = fs.readdirSync(folder); + if (files.length > 0) { + files.forEach(function (file) { + var fullPath = path.join(folder, file); + cleanEmptyFoldersRecursively(fullPath); + }); - if (files.length == 0) { - fs.rmdirSync(folder); - return; - } + // re-evaluate files; after deleting subfolder + // we may have parent folder empty now + files = fs.readdirSync(folder); + } + + if (files.length == 0) { + fs.rmdirSync(folder); + return; + } } async function deleteFilesWithoutErrorsTypescript(stdout) { - let errors = stdout.split("\n"); - let fileNames = []; - - /** - * NOTE that because of how typescript reports file paths, all paths in - * fileNames will start from /snippets and not from the root (~/) - */ - errors.forEach(error => { - // tsc output has some other lines + info that we are not interested in - if (error !== "" && error.includes("snippets")) { - fileNames.push(error.split("(")[0]); - } - }) - - let snippetsPathPrefix = "src/plugins/codeTypeChecking/jsEnv/snippets/"; - let files = await getFiles(snippetsPathPrefix); - await getRootDir(); - - files.forEach(file => { - let actualPath = file.replace(rootDir, ""); - - // We replace the path from project root to snippets with just snippets/ to match the format that tsc outputs - if (!fileNames.includes(actualPath.replace(snippetsPathPrefix, "snippets/"))) { - let exists = fs.existsSync(actualPath); - if (exists) { - fs.rmSync(actualPath); - } - } - }); + let errors = stdout.split("\n"); + let fileNames = []; + + /** + * NOTE that because of how typescript reports file paths, all paths in + * fileNames will start from /snippets and not from the root (~/) + */ + errors.forEach((error) => { + // tsc output has some other lines + info that we are not interested in + if (error !== "" && error.includes("snippets")) { + fileNames.push(error.split("(")[0]); + } + }); + + let snippetsPathPrefix = "src/plugins/codeTypeChecking/jsEnv/snippets/"; + let files = await getFiles(snippetsPathPrefix); + await getRootDir(); + + files.forEach((file) => { + let actualPath = file.replace(rootDir, ""); + + // We replace the path from project root to snippets with just snippets/ to match the format that tsc outputs + if ( + !fileNames.includes(actualPath.replace(snippetsPathPrefix, "snippets/")) + ) { + let exists = fs.existsSync(actualPath); + if (exists) { + fs.rmSync(actualPath); + } + } + }); - cleanEmptyFoldersRecursively("src/plugins/codeTypeChecking/jsEnv/snippets"); + cleanEmptyFoldersRecursively("src/plugins/codeTypeChecking/jsEnv/snippets"); } - async function deleteFilesWithoutErrorsPython(stdout) { - let errors = stdout.split("\n"); - let fileNames = []; + let errors = stdout.split("\n"); + let fileNames = []; - errors.forEach(error => { - if (error !== "" && error.includes("snippets")) { - fileNames.push(error.split(":")[0].trim()); - } - }) - - let snippetsPathPrefix = "src/plugins/codeTypeChecking/pythonEnv/snippets/"; - let files = await getFiles(snippetsPathPrefix); - await getRootDir(); - - files.forEach(file => { - if (!fileNames.includes(file)) { - let exists = fs.existsSync(file); - if (exists) { - fs.rmSync(file); - } - } - }); + errors.forEach((error) => { + if (error !== "" && error.includes("snippets")) { + fileNames.push(error.split(":")[0].trim()); + } + }); + + let snippetsPathPrefix = "src/plugins/codeTypeChecking/pythonEnv/snippets/"; + let files = await getFiles(snippetsPathPrefix); + await getRootDir(); + + files.forEach((file) => { + if (!fileNames.includes(file)) { + let exists = fs.existsSync(file); + if (exists) { + fs.rmSync(file); + } + } + }); - cleanEmptyFoldersRecursively("src/plugins/codeTypeChecking/pythonEnv/snippets"); + cleanEmptyFoldersRecursively( + "src/plugins/codeTypeChecking/pythonEnv/snippets" + ); } async function checkCodeSnippets(language) { - // typescript.. - if (language === "typescript") { - await new Promise((res, rej) => { - exec("cd src/plugins/codeTypeChecking/jsEnv/ && npm run test", async function (err, stdout, stderr) { - await deleteFilesWithoutErrorsTypescript(stdout); - if (err) { - console.log('\x1b[31m%s\x1b[0m', stdout); - console.log('\x1b[31m%s\x1b[0m', err); - console.log("=======SETUP INSTRS========\n"); - console.log('\x1b[36m%s\x1b[0m', `To setup a JS env, run the following (from v2 folder): + // typescript.. + if (language === "typescript") { + await new Promise((res, rej) => { + exec( + "cd src/plugins/codeTypeChecking/jsEnv/ && npm run test", + async function (err, stdout, stderr) { + await deleteFilesWithoutErrorsTypescript(stdout); + if (err) { + console.log("\x1b[31m%s\x1b[0m", stdout); + console.log("\x1b[31m%s\x1b[0m", err); + console.log("=======SETUP INSTRS========\n"); + console.log( + "\x1b[36m%s\x1b[0m", + `To setup a JS env, run the following (from v2 folder): - cd src/plugins/codeTypeChecking/jsEnv/ - npm i - - npm run test (to make sure that it's setup correctly)`) - console.log("==========================\n"); + - npm run test (to make sure that it's setup correctly)` + ); + console.log("==========================\n"); - return rej(err); - } - res(); - }); - }) - } else if (language === "go") { - await new Promise((res, rej) => { - exec("cd src/plugins/codeTypeChecking/goEnv/ && go mod tidy && go build ./...", function (err, stdout, stderr) { - if (err) { - console.log('\x1b[31m%s\x1b[0m', stdout); - console.log('\x1b[31m%s\x1b[0m', err); - console.log("=======SETUP INSTRS========\n"); - console.log('\x1b[36m%s\x1b[0m', `Make sure that you have go installed on your system and try this command again`) - console.log("==========================\n"); - return rej(err); - } - res(); - }); - }) - } else if (language === "python") { - await new Promise((res, rej) => { - exec("cd src/plugins/codeTypeChecking/pythonEnv/ && ./checkSnippets.sh", async function (err, stdout, stderr) { - await deleteFilesWithoutErrorsPython(stdout); - if (err) { - console.log('\x1b[31m%s\x1b[0m', stdout); - console.log('\x1b[31m%s\x1b[0m', err); - console.log("=======SETUP INSTRS========\n"); - console.log('\x1b[36m%s\x1b[0m', `To setup a python env, run the following (from v2 folder): + return rej(err); + } + res(); + } + ); + }); + } else if (language === "go") { + await new Promise((res, rej) => { + exec( + "cd src/plugins/codeTypeChecking/goEnv/ && go mod tidy && go build ./...", + function (err, stdout, stderr) { + if (err) { + console.log("\x1b[31m%s\x1b[0m", stdout); + console.log("\x1b[31m%s\x1b[0m", err); + console.log("=======SETUP INSTRS========\n"); + console.log( + "\x1b[36m%s\x1b[0m", + `Make sure that you have go installed on your system and try this command again` + ); + console.log("==========================\n"); + return rej(err); + } + res(); + } + ); + }); + } else if (language === "python") { + await new Promise((res, rej) => { + exec( + "cd src/plugins/codeTypeChecking/pythonEnv/ && ./checkSnippets.sh", + async function (err, stdout, stderr) { + await deleteFilesWithoutErrorsPython(stdout); + if (err) { + console.log("\x1b[31m%s\x1b[0m", stdout); + console.log("\x1b[31m%s\x1b[0m", err); + console.log("=======SETUP INSTRS========\n"); + console.log( + "\x1b[36m%s\x1b[0m", + `To setup a python env, run the following (from v2 folder): - cd src/plugins/codeTypeChecking/pythonEnv/ - virtualenv ./venv - source venv/bin/activate - pip install -r requirements.txt - - pylint ./snippets (to make sure that it's setup correctly)`) - console.log("==========================\n"); - return rej(err); - } - res(); - }); - }) - } else if (language === "kotlin") { - await new Promise((res, rej) => { - exec("cd src/plugins/codeTypeChecking/kotlinEnv/ && ./gradlew build", async function (err, stdout, stderr) { - if (err) { - console.log('\x1b[31m%s\x1b[0m', stdout); - console.log('\x1b[31m%s\x1b[0m', err); - console.log("=======SETUP INSTRS========\n"); - console.log('\x1b[36m%s\x1b[0m', `Make sure that you have kotlin installed on your system and try this command again`) - console.log("==========================\n"); - return rej(err); - } - res(); - }); - }) - } else if (language === "swift") { - await new Promise((res, rej) => { - exec("cd src/plugins/codeTypeChecking/iosenv/ && set -o pipefail && xcodebuild -workspace iosenv.xcworkspace -scheme iosenv build CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO -quiet | ./Pods/xcbeautify/xcbeautify", async function (err, stdout, stderr) { - if (err) { - console.log('\x1b[31m%s\x1b[0m', stdout); - console.log('\x1b[31m%s\x1b[0m', err); - console.log("=======SETUP INSTRS========\n"); - console.log('\x1b[36m%s\x1b[0m', `Make sure that you have Xcode installed on your system and try this command again`) - console.log('\x1b[36m%s\x1b[0m', `Make sure that you have run 'pod install' inside iosenv and try this command again`) - console.log("==========================\n"); - return rej(err); - } - res(); - }); - }) - } else if (language === "dart") { - await new Promise((res, rej) => { - exec("cd src/plugins/codeTypeChecking/dart_env/ && flutter build web -t lib/snippets/main.dart", async function (err, stdout, stderr) { - if (err) { - console.log('\x1b[31m%s\x1b[0m', stdout); - console.log('\x1b[31m%s\x1b[0m', err); - console.log("=======SETUP INSTRS========\n"); - console.log('\x1b[36m%s\x1b[0m', `Make sure that you have flutter setup on your system and try this command again`) - console.log('\x1b[36m%s\x1b[0m', `Make sure that you have run 'flutter pub get' inside dart_env and try this command again`) - console.log("==========================\n"); - return rej(err); - } - res(); - }); - }) - } else { - throw new Error("Unsupported language in checkCodeSnippets"); - } + - pylint ./snippets (to make sure that it's setup correctly)` + ); + console.log("==========================\n"); + return rej(err); + } + res(); + } + ); + }); + } else if (language === "kotlin") { + await new Promise((res, rej) => { + exec( + "cd src/plugins/codeTypeChecking/kotlinEnv/ && ./gradlew build", + async function (err, stdout, stderr) { + if (err) { + console.log("\x1b[31m%s\x1b[0m", stdout); + console.log("\x1b[31m%s\x1b[0m", err); + console.log("=======SETUP INSTRS========\n"); + console.log( + "\x1b[36m%s\x1b[0m", + `Make sure that you have kotlin installed on your system and try this command again` + ); + console.log("==========================\n"); + return rej(err); + } + res(); + } + ); + }); + } else if (language === "swift") { + await new Promise((res, rej) => { + exec( + "cd src/plugins/codeTypeChecking/iosenv/ && set -o pipefail && xcodebuild -workspace iosenv.xcworkspace -scheme iosenv build CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO -quiet | ./Pods/xcbeautify/xcbeautify", + async function (err, stdout, stderr) { + if (err) { + console.log("\x1b[31m%s\x1b[0m", stdout); + console.log("\x1b[31m%s\x1b[0m", err); + console.log("=======SETUP INSTRS========\n"); + console.log( + "\x1b[36m%s\x1b[0m", + `Make sure that you have Xcode installed on your system and try this command again` + ); + console.log( + "\x1b[36m%s\x1b[0m", + `Make sure that you have run 'pod install' inside iosenv and try this command again` + ); + console.log("==========================\n"); + return rej(err); + } + res(); + } + ); + }); + } else if (language === "dart") { + await new Promise((res, rej) => { + exec( + "cd src/plugins/codeTypeChecking/dart_env/ && flutter build web -t lib/snippets/main.dart", + async function (err, stdout, stderr) { + if (err) { + console.log("\x1b[31m%s\x1b[0m", stdout); + console.log("\x1b[31m%s\x1b[0m", err); + console.log("=======SETUP INSTRS========\n"); + console.log( + "\x1b[36m%s\x1b[0m", + `Make sure that you have flutter setup on your system and try this command again` + ); + console.log( + "\x1b[36m%s\x1b[0m", + `Make sure that you have run 'flutter pub get' inside dart_env and try this command again` + ); + console.log("==========================\n"); + return rej(err); + } + res(); + } + ); + }); + } else { + throw new Error("Unsupported language in checkCodeSnippets"); + } } let rootDir = undefined; // uptil v2 async function getRootDir() { - if (rootDir === undefined) { - rootDir = await new Promise(res => exec("pwd", function (err, stdout, stderr) { - res(stdout); - })); - rootDir = rootDir.trim() + "/"; - // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ - } - - return rootDir; + if (rootDir === undefined) { + rootDir = await new Promise((res) => + exec("pwd", function (err, stdout, stderr) { + res(stdout); + }) + ); + rootDir = rootDir.trim() + "/"; + // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ + } + + return rootDir; } async function getRecipeName(mdFile) { - if (rootDir === undefined) { - rootDir = await new Promise(res => exec("pwd", function (err, stdout, stderr) { - res(stdout); - })); - rootDir = rootDir.trim() + "/"; - // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ - } - let postV2 = mdFile.replace(rootDir, ""); - return postV2.split("/")[0]; + if (rootDir === undefined) { + rootDir = await new Promise((res) => + exec("pwd", function (err, stdout, stderr) { + res(stdout); + }) + ); + rootDir = rootDir.trim() + "/"; + // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ + } + let postV2 = mdFile.replace(rootDir, ""); + return postV2.split("/")[0]; } -async function addCodeSnippetToEnvHelper(codeSnippet, language, mdFile, codeBlockCountInFile) { - // we replace all the variables here so that the code can compile: - - // Excluding __OMIT_START__ and __OMIT_END__ tags from the code snippet before running the type check plugin - // We need to do this before intializing ogCodeSnippet to prevent false warnings about not using docs variables - codeSnippet = codeSnippet.replace(/__OMIT_START__([\s\S]*?)__OMIT_END__/g, '$1'); - - let ogCodeSnippet = codeSnippet; - codeSnippet = codeSnippet.replaceAll("^{coreInjector_connection_uri_comment}", ""); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_uri}", "\"\","); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key_commented}", ""); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key}", "\"\""); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_connection_uri_comment_with_hash}", "") - codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key_commented_with_hash}", "") - - codeSnippet = codeSnippet.replaceAll("^{form_flowType}", "USER_INPUT_CODE_AND_MAGIC_LINK"); - codeSnippet = codeSnippet.replaceAll("^{form_contactMethod}", "PHONE"); - codeSnippet = codeSnippet.replaceAll("^{form_contactMethod_initialize_Python}", "ContactPhoneOnlyConfig"); - codeSnippet = codeSnippet.replaceAll("^{form_contactMethod_import_Python}", "from supertokens_python.recipe.passwordless import ContactPhoneOnlyConfig"); - codeSnippet = codeSnippet.replaceAll("^{form_contactMethod_sendCB_Go}", - `ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ +async function addCodeSnippetToEnvHelper( + codeSnippet, + language, + mdFile, + codeBlockCountInFile +) { + // we replace all the variables here so that the code can compile: + + // Excluding __OMIT_START__ and __OMIT_END__ tags from the code snippet before running the type check plugin + // We need to do this before intializing ogCodeSnippet to prevent false warnings about not using docs variables + codeSnippet = codeSnippet.replace( + /__OMIT_START__([\s\S]*?)__OMIT_END__/g, + "$1" + ); + + let ogCodeSnippet = codeSnippet; + codeSnippet = codeSnippet.replaceAll( + "^{coreInjector_connection_uri_comment}", + "" + ); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_uri}", '"",'); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key_commented}", ""); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key}", '""'); + codeSnippet = codeSnippet.replaceAll( + "^{coreInjector_connection_uri_comment_with_hash}", + "" + ); + codeSnippet = codeSnippet.replaceAll( + "^{coreInjector_api_key_commented_with_hash}", + "" + ); + + codeSnippet = codeSnippet.replaceAll( + "^{form_flowType}", + "USER_INPUT_CODE_AND_MAGIC_LINK" + ); + codeSnippet = codeSnippet.replaceAll("^{form_contactMethod}", "PHONE"); + codeSnippet = codeSnippet.replaceAll( + "^{form_contactMethod_initialize_Python}", + "ContactPhoneOnlyConfig" + ); + codeSnippet = codeSnippet.replaceAll( + "^{form_contactMethod_import_Python}", + "from supertokens_python.recipe.passwordless import ContactPhoneOnlyConfig" + ); + codeSnippet = codeSnippet.replaceAll( + "^{form_contactMethod_sendCB_Go}", + `ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ Enabled: true, -},`); - - let recipeName = await getRecipeName(mdFile); - let replaceMap = { ...mdVars["common"], ...mdVars[recipeName] }; - if (replaceMap !== undefined) { - let keys = Object.keys(replaceMap); - for (let i = 0; i < keys.length; i++) { - if (codeSnippet.includes(`^{${keys[i]}}`)) { - codeSnippet = codeSnippet.replaceAll(`^{${keys[i]}}`, replaceMap[keys[i]]); - } - } +},` + ); + + let recipeName = await getRecipeName(mdFile); + let replaceMap = { ...mdVars["common"], ...mdVars[recipeName] }; + if (replaceMap !== undefined) { + let keys = Object.keys(replaceMap); + for (let i = 0; i < keys.length; i++) { + if (codeSnippet.includes(`^{${keys[i]}}`)) { + codeSnippet = codeSnippet.replaceAll( + `^{${keys[i]}}`, + replaceMap[keys[i]] + ); + } } - - // this block is there so that the dev knows that the resulting block should use variable code snippets. - if (ogCodeSnippet !== codeSnippet) { - for (let i = 0; i < 50; i++) { - if (language === "typescript" || language === "go" || language === "kotlin" || language === "swift" || language === "dart") { - codeSnippet = "// THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + codeSnippet; - } else if (language === "python") { - codeSnippet = "# THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + codeSnippet; - } else { - throw new Error("language not supported"); - } - } + } + + // this block is there so that the dev knows that the resulting block should use variable code snippets. + if (ogCodeSnippet !== codeSnippet) { + for (let i = 0; i < 50; i++) { + if ( + language === "typescript" || + language === "go" || + language === "kotlin" || + language === "swift" || + language === "dart" + ) { + codeSnippet = + "// THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + + codeSnippet; + } else if (language === "python") { + codeSnippet = + "# THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + + codeSnippet; + } else { + throw new Error("language not supported"); + } } - - - if (language === "typescript") { - if (codeSnippet.includes("require(")) { - throw new Error("Do not use 'require' in TS code. Error in " + mdFile); + } + + if (language === "typescript") { + if (codeSnippet.includes("require(")) { + // except for the attack protection suite , where we need to allow require for compatibility reasons, + // as the SDK URL is dynamic and import might break some builds + if (!codeSnippet.includes('require("https://deviceid.supertokens.io')) { + throw new Error("Do not use 'require' in TS code. Error in " + mdFile); } - codeSnippet = `export { }\n// Original: ${mdFile}\n${codeSnippet}`; // see https://www.aritsltd.com/blog/frontend-development/cannot-redeclare-block-scoped-variable-the-reason-behind-the-error-and-the-way-to-resolve-it/ + } + } + codeSnippet = `export { }\n// Original: ${mdFile}\n${codeSnippet}`; // see https://www.aritsltd.com/blog/frontend-development/cannot-redeclare-block-scoped-variable-the-reason-behind-the-error-and-the-way-to-resolve-it/ - // vue requires use of ", ""); - } + // vue requires use of ", ""); + } - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - await new Promise(async (res, rej) => { - fs.mkdir('src/plugins/codeTypeChecking/jsEnv/snippets/' + folderName, { recursive: true }, async (err) => { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/jsEnv/snippets/" + folderName, + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake( + "src/plugins/codeTypeChecking/jsEnv/snippets/" + + folderName + + "/index.tsx", + codeSnippet + ); + fs.writeFile( + "src/plugins/codeTypeChecking/jsEnv/snippets/" + + folderName + + "/index.tsx", + codeSnippet, + function (err) { if (err) { - rej(err); + rej(err); } else { - await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/jsEnv/snippets/' + folderName + "/index.tsx", codeSnippet); - fs.writeFile('src/plugins/codeTypeChecking/jsEnv/snippets/' + folderName + "/index.tsx", codeSnippet, function (err) { - if (err) { - rej(err); - } else { - res(); - } - }); + res(); } - }); - }); - } else if (language === "go") { - if (codeSnippet.includes("/supertokens-go/") || codeSnippet.includes("/supertokens-go\n")) { - throw new Error("Do not use supertokens-go package. Use supertokens-golang package. Error in " + mdFile); + } + ); + } } - // we change the last folder path dir to be a valid go module name - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - let splittedFolder = folderName.split("/"); - let lastDir = splittedFolder[splittedFolder.length - 1]; - lastDir = lastDir.replaceAll("-", "").replaceAll(".", ""); - splittedFolder[splittedFolder.length - 1] = lastDir; - let newFolderName = splittedFolder.join("/"); - - // adding package on top of go file - codeSnippet = `package ${lastDir}\n/*\n${mdFile}\n*/\n${codeSnippet}`; - - await new Promise(async (res, rej) => { - fs.mkdir('src/plugins/codeTypeChecking/goEnv/snippets/' + newFolderName, { recursive: true }, async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/goEnv/snippets/' + newFolderName + "/main.go", codeSnippet); - fs.writeFile('src/plugins/codeTypeChecking/goEnv/snippets/' + newFolderName + "/main.go", codeSnippet, function (err) { - if (err) { - rej(err); - } else { - res(); - } - }); - } - }); - }); - } else if (language === "python") { - codeSnippet = `# ${mdFile}\n${codeSnippet}` - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - await new Promise(async (res, rej) => { - fs.mkdir('src/plugins/codeTypeChecking/pythonEnv/snippets/' + folderName, { recursive: true }, async (err) => { + ); + }); + } else if (language === "go") { + if ( + codeSnippet.includes("/supertokens-go/") || + codeSnippet.includes("/supertokens-go\n") + ) { + throw new Error( + "Do not use supertokens-go package. Use supertokens-golang package. Error in " + + mdFile + ); + } + // we change the last folder path dir to be a valid go module name + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + let splittedFolder = folderName.split("/"); + let lastDir = splittedFolder[splittedFolder.length - 1]; + lastDir = lastDir.replaceAll("-", "").replaceAll(".", ""); + splittedFolder[splittedFolder.length - 1] = lastDir; + let newFolderName = splittedFolder.join("/"); + + // adding package on top of go file + codeSnippet = `package ${lastDir}\n/*\n${mdFile}\n*/\n${codeSnippet}`; + + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/goEnv/snippets/" + newFolderName, + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake( + "src/plugins/codeTypeChecking/goEnv/snippets/" + + newFolderName + + "/main.go", + codeSnippet + ); + fs.writeFile( + "src/plugins/codeTypeChecking/goEnv/snippets/" + + newFolderName + + "/main.go", + codeSnippet, + function (err) { if (err) { - rej(err); + rej(err); } else { - await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/pythonEnv/snippets/' + folderName + "/main.py", codeSnippet); - fs.writeFile('src/plugins/codeTypeChecking/pythonEnv/snippets/' + folderName + "/main.py", codeSnippet, function (err) { - if (err) { - rej(err); - } else { - res(); - } - }); + res(); } - }); - }); - } else if (language === "kotlin") { - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - folderName = folderName.replace(".mdx", ""); - let packageNameSplitted = folderName.split("/"); - packageNameSplitted = packageNameSplitted.map(i => { - if (i.includes("-")) { - return "`" + i + "`" - } - return i; - }) - codeSnippet = `package com.example.myapplication${packageNameSplitted.join(".")}\n\n// Original: ${mdFile}\n${codeSnippet}`; - await new Promise(async (res, rej) => { - fs.mkdir('src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/' + folderName, { recursive: true }, async (err) => { + } + ); + } + } + ); + }); + } else if (language === "python") { + codeSnippet = `# ${mdFile}\n${codeSnippet}`; + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/pythonEnv/snippets/" + folderName, + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake( + "src/plugins/codeTypeChecking/pythonEnv/snippets/" + + folderName + + "/main.py", + codeSnippet + ); + fs.writeFile( + "src/plugins/codeTypeChecking/pythonEnv/snippets/" + + folderName + + "/main.py", + codeSnippet, + function (err) { if (err) { - rej(err); + rej(err); } else { - await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/' + folderName + "/Code.kt", codeSnippet); - fs.writeFile('src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/' + folderName + "/Code.kt", codeSnippet, function (err) { - if (err) { - rej(err); - } else { - res(); - } - }); + res(); } - }); - }); - } else if (language === "swift") { - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - folderName = folderName.replace(".mdx", ""); - let packageNameSplitted = folderName.split("/"); - packageNameSplitted = packageNameSplitted.map(i => { - if (i.includes("-")) { - return "`" + i + "`" - } - return i; - }) - codeSnippet = `// Original: ${mdFile}\n${codeSnippet}`; - const folderNameSplit = folderName.split("/"); - const lastFolderPath = folderNameSplit[folderNameSplit.length - 1] + new Date().getTime() - await new Promise(async (res, rej) => { - fs.mkdir('src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/' + folderName, { recursive: true }, async (err) => { + } + ); + } + } + ); + }); + } else if (language === "kotlin") { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + folderName = folderName.replace(".mdx", ""); + let packageNameSplitted = folderName.split("/"); + packageNameSplitted = packageNameSplitted.map((i) => { + if (i.includes("-")) { + return "`" + i + "`"; + } + return i; + }); + codeSnippet = `package com.example.myapplication${packageNameSplitted.join( + "." + )}\n\n// Original: ${mdFile}\n${codeSnippet}`; + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/" + + folderName, + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake( + "src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/" + + folderName + + "/Code.kt", + codeSnippet + ); + fs.writeFile( + "src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/" + + folderName + + "/Code.kt", + codeSnippet, + function (err) { if (err) { - rej(err); + rej(err); } else { - await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/' + folderName + "/Code.swift", codeSnippet); - const filename = 'src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/' + folderName + `/${lastFolderPath}.swift`; - fs.writeFile(filename, codeSnippet, function (err) { - if (err) { - rej(err); - } else { - execSync(`./src/plugins/codeTypeChecking/iosenv/createFile.rb "${filename}"`) - res(); - } - }); + res(); } + } + ); + } + } + ); + }); + } else if (language === "swift") { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + folderName = folderName.replace(".mdx", ""); + let packageNameSplitted = folderName.split("/"); + packageNameSplitted = packageNameSplitted.map((i) => { + if (i.includes("-")) { + return "`" + i + "`"; + } + return i; + }); + codeSnippet = `// Original: ${mdFile}\n${codeSnippet}`; + const folderNameSplit = folderName.split("/"); + const lastFolderPath = + folderNameSplit[folderNameSplit.length - 1] + new Date().getTime(); + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/" + folderName, + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake( + "src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/" + + folderName + + "/Code.swift", + codeSnippet + ); + const filename = + "src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/" + + folderName + + `/${lastFolderPath}.swift`; + fs.writeFile(filename, codeSnippet, function (err) { + if (err) { + rej(err); + } else { + execSync( + `./src/plugins/codeTypeChecking/iosenv/createFile.rb "${filename}"` + ); + res(); + } }); - }); - } else if (language === "dart") { - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + } + } + ); + }); + } else if (language === "dart") { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/dart_env/lib/snippets", + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + res(); + } + } + ); + }); - await new Promise(async (res, rej) => { - fs.mkdir("src/plugins/codeTypeChecking/dart_env/lib/snippets", { recursive: true }, async (err) => { - if (err) { - rej(err); - } else { - res(); - } - }) - }); + await new Promise(async (res, rej) => { + fs.writeFile( + "src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", + mainContent, + async (err) => { + if (err) { + rej(err); + } else { + res(); + } + } + ); + }); - await new Promise(async (res, rej) => { - fs.writeFile("src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", mainContent, async (err) => { + await new Promise(async (res, rej) => { + fs.mkdir( + "src/plugins/codeTypeChecking/dart_env/lib/snippets/" + folderName, + { recursive: true }, + async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake( + "src/plugins/codeTypeChecking/dart_env/lib/snippets/" + + folderName + + "/index.dart", + codeSnippet + ); + fs.writeFile( + "src/plugins/codeTypeChecking/dart_env/lib/snippets/" + + folderName + + "/index.dart", + codeSnippet, + function (err) { if (err) { - rej(err); + rej(err); } else { - res(); + res(); } - }) - }); - - await new Promise(async (res, rej) => { - fs.mkdir('src/plugins/codeTypeChecking/dart_env/lib/snippets/' + folderName, { recursive: true }, async (err) => { - if (err) { + } + ); + + let dartImportStatement = `import 'package:dart_env/snippets/${folderName}/index.dart';`; + mainContent = dartImportStatement + "\n" + mainContent; + + await new Promise(async (res, rej) => { + fs.writeFile( + "src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", + mainContent, + async (err) => { + if (err) { rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/dart_env/lib/snippets/' + folderName + "/index.dart", codeSnippet); - fs.writeFile('src/plugins/codeTypeChecking/dart_env/lib/snippets/' + folderName + "/index.dart", codeSnippet, function (err) { - if (err) { - rej(err); - } else { - res(); - } - }); - - let dartImportStatement = `import 'package:dart_env/snippets/${folderName}/index.dart';`; - mainContent = dartImportStatement + "\n" + mainContent; - - await new Promise(async (res, rej) => { - fs.writeFile("src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", mainContent, async (err) => { - if (err) { - rej(err); - } else { - res(); - } - }) - }); + } else { + res(); + } } + ); }); - }); - } else { - throw new Error("Unsupported language in addCodeSnippetToEnvHelper"); - } + } + } + ); + }); + } else { + throw new Error("Unsupported language in addCodeSnippetToEnvHelper"); + } } -async function assertThatUserIsNotRemovedDocsVariableByMistake(path, codeSnippet) { - /* first we try and read the contents to see if this has +async function assertThatUserIsNotRemovedDocsVariableByMistake( + path, + codeSnippet +) { + /* first we try and read the contents to see if this has THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE and if the new code snippet doesn't then, the dev has made a mistake of removing variables...*/ - return new Promise((res, rej) => { - fs.readFile(path, 'utf8', function (err, data) { - if (data !== undefined) { - if (data.includes("THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE") && !codeSnippet.includes("THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE")) { - let message = "DID YOU FORGET TO USE DOCS VARIABLES IN A RECENT CODE CHANGE? PLEASE CHECK" - + "\n\nIf you think this error is unrelated to your changes, try deleting the `snippets` folder for all languages and run again.\n\nThe file path is: " + path - return rej(new Error(message)); - } - } - res(); - }); - }) + return new Promise((res, rej) => { + fs.readFile(path, "utf8", function (err, data) { + if (data !== undefined) { + if ( + data.includes( + "THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE" + ) && + !codeSnippet.includes( + "THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE" + ) + ) { + let message = + "DID YOU FORGET TO USE DOCS VARIABLES IN A RECENT CODE CHANGE? PLEASE CHECK" + + "\n\nIf you think this error is unrelated to your changes, try deleting the `snippets` folder for all languages and run again.\n\nThe file path is: " + + path; + return rej(new Error(message)); + } + } + res(); + }); + }); } function replaceCustomPlaceholdersInLine(child, exportedVariables) { - // A child will either have value properties or more children - - // If it has a value, check if it is using a variable. If it is then replace otherwise skip - if (child.value) { - var valueCopy = child.value; - - // Exclude sections marked by __OMIT_START__ and __OMIT_END__ from the code snippet, along with the tags themselves - valueCopy = valueCopy.replace(/__OMIT_START__[\s\S]*?__OMIT_END__\n?/g, ''); - - let eachLine = valueCopy.split("\n"); - let newLines = []; - for (let i = 0; i < eachLine.length; i++) { - let line = eachLine[i]; - // If the line contains ts-ignore or ends with a comment indicating it should be removed - if (line.includes("@ts-ignore") || /(\/\/|#) typecheck-only, removed from output$/.test(line)) { - continue; - } - - /** - * For snippets that use an older version of supertokens-node we use supertokens-node7 to import - * If the import contains supertokens-node7 we replace it with supertokens-node for the final - * rendered snippet - */ - if (line.includes("supertokens-node7")) { - line = line.split("supertokens-node7").join("supertokens-node"); - newLines.push(line); - continue; - } - - /** - * For snippets that contain supertokensUIInit, we add the dic id param parameter - */ - if (line.includes("supertokensUIInit(")) { - line = line.split("supertokensUIInit(").join("(window as any).supertokensUIInit(\"supertokensui\", "); - newLines.push(line); - continue; - } - if (line.includes("supertokensUI") && !line.includes("supertokens-auth-react-script")) { - line = line.split("supertokensUI").join("(window as any).supertokensUI"); - newLines.push(line); - continue; - } - - /** - * For snippets that use v5 react-router-dom we use react-router-dom5 to import - * If the import contains react-router-dom5 we replace it with react-router-dom for the final - * rendered snippet - */ - if (line.includes("react-router-dom5")) { - line = line.split("react-router-dom5").join("react-router-dom"); - newLines.push(line); - continue; - } - - /** - * For python code snippets that contain "# type: ignore", we remove that - * string snippet from the line - */ - if (line.includes("# type: ignore")) { - line = line.split("# type: ignore").join(""); - newLines.push(line); - continue; - } - if (line.includes("#type: ignore")) { - line = line.split("#type: ignore").join(""); - newLines.push(line); - continue; - } - if (line.includes("# type:ignore")) { - line = line.split("# type:ignore").join(""); - newLines.push(line); - continue; - } - if (line.includes("#type:ignore")) { - line = line.split("#type:ignore").join(""); - newLines.push(line); - continue; - } - - // if the line contains "# pyright: ", then we remove that line - if (line.includes("# pyright:") || line.includes("#pyright:")) { - continue; - } - - /** - * For snippets that use supertokens-web-js as an HTML script we import supertokens-web-js-script and supertokens-auth-react-script for types. - * If the line contains this we skip adding the line - */ - if (line.includes("supertokens-web-js-script") || line.includes("supertokens-auth-react-script")) { - continue; - } - - /** - * For iOS snippets we need to use fileprivate class to avoid build errors in Xcode due to duplicate declarations. - * This replaces the fileprivate declaration with a simple class declaration in the final output - */ - if (line.includes("fileprivate class")) { - line = line.split("fileprivate class").join("class"); - newLines.push(line); - continue; - } - - newLines.push(eachLine[i]); - } - - child.value = newLines.join("\n"); + // A child will either have value properties or more children + + // If it has a value, check if it is using a variable. If it is then replace otherwise skip + if (child.value) { + var valueCopy = child.value; + + // Exclude sections marked by __OMIT_START__ and __OMIT_END__ from the code snippet, along with the tags themselves + valueCopy = valueCopy.replace(/__OMIT_START__[\s\S]*?__OMIT_END__\n?/g, ""); + + let eachLine = valueCopy.split("\n"); + let newLines = []; + for (let i = 0; i < eachLine.length; i++) { + let line = eachLine[i]; + // If the line contains ts-ignore or ends with a comment indicating it should be removed + if ( + line.includes("@ts-ignore") || + /(\/\/|#) typecheck-only, removed from output$/.test(line) + ) { + continue; + } + + /** + * For snippets that use an older version of supertokens-node we use supertokens-node7 to import + * If the import contains supertokens-node7 we replace it with supertokens-node for the final + * rendered snippet + */ + if (line.includes("supertokens-node7")) { + line = line.split("supertokens-node7").join("supertokens-node"); + newLines.push(line); + continue; + } + + /** + * For snippets that contain supertokensUIInit, we add the dic id param parameter + */ + if (line.includes("supertokensUIInit(")) { + line = line + .split("supertokensUIInit(") + .join('(window as any).supertokensUIInit("supertokensui", '); + newLines.push(line); + continue; + } + if ( + line.includes("supertokensUI") && + !line.includes("supertokens-auth-react-script") + ) { + line = line + .split("supertokensUI") + .join("(window as any).supertokensUI"); + newLines.push(line); + continue; + } + + /** + * For snippets that use v5 react-router-dom we use react-router-dom5 to import + * If the import contains react-router-dom5 we replace it with react-router-dom for the final + * rendered snippet + */ + if (line.includes("react-router-dom5")) { + line = line.split("react-router-dom5").join("react-router-dom"); + newLines.push(line); + continue; + } + + /** + * For python code snippets that contain "# type: ignore", we remove that + * string snippet from the line + */ + if (line.includes("# type: ignore")) { + line = line.split("# type: ignore").join(""); + newLines.push(line); + continue; + } + if (line.includes("#type: ignore")) { + line = line.split("#type: ignore").join(""); + newLines.push(line); + continue; + } + if (line.includes("# type:ignore")) { + line = line.split("# type:ignore").join(""); + newLines.push(line); + continue; + } + if (line.includes("#type:ignore")) { + line = line.split("#type:ignore").join(""); + newLines.push(line); + continue; + } + + // if the line contains "# pyright: ", then we remove that line + if (line.includes("# pyright:") || line.includes("#pyright:")) { + continue; + } + + /** + * For snippets that use supertokens-web-js as an HTML script we import supertokens-web-js-script and supertokens-auth-react-script for types. + * If the line contains this we skip adding the line + */ + if ( + line.includes("supertokens-web-js-script") || + line.includes("supertokens-auth-react-script") + ) { + continue; + } + + /** + * For iOS snippets we need to use fileprivate class to avoid build errors in Xcode due to duplicate declarations. + * This replaces the fileprivate declaration with a simple class declaration in the final output + */ + if (line.includes("fileprivate class")) { + line = line.split("fileprivate class").join("class"); + newLines.push(line); + continue; + } + + newLines.push(eachLine[i]); } - // If it has children then repeat recursively - if (child.children) { - child.children = child.children.map(subChild => { - return replaceCustomPlaceholdersInLine(subChild, exportedVariables); - }) - } + child.value = newLines.join("\n"); + } + + // If it has children then repeat recursively + if (child.children) { + child.children = child.children.map((subChild) => { + return replaceCustomPlaceholdersInLine(subChild, exportedVariables); + }); + } - return child; + return child; } -module.exports = { addCodeSnippetToEnv, checkCodeSnippets, replaceCustomPlaceholdersInLine } \ No newline at end of file +module.exports = { + addCodeSnippetToEnv, + checkCodeSnippets, + replaceCustomPlaceholdersInLine, +}; From 8ef1bf53bcbdbe80a6d69ee2aa18e04247206e05 Mon Sep 17 00:00:00 2001 From: Victor Bojica Date: Tue, 15 Oct 2024 18:19:13 +0300 Subject: [PATCH 2/4] revert auto formatting --- v2/src/plugins/codeTypeChecking/index.js | 1510 +++++++++------------- 1 file changed, 611 insertions(+), 899 deletions(-) diff --git a/v2/src/plugins/codeTypeChecking/index.js b/v2/src/plugins/codeTypeChecking/index.js index 41946c08e..1cc0fbf77 100644 --- a/v2/src/plugins/codeTypeChecking/index.js +++ b/v2/src/plugins/codeTypeChecking/index.js @@ -1,10 +1,10 @@ -let fs = require("fs"); -let readdir = require("fs").promises.readdir; -let path = require("path"); -var exec = require("child_process").exec; -var crypto = require("crypto"); +let fs = require('fs'); +let readdir = require("fs").promises.readdir +let path = require('path'); +var exec = require('child_process').exec; +var crypto = require('crypto'); let mdVars = require("../markdownVariables.json"); -const { execSync } = require("child_process"); +const { execSync } = require('child_process'); let defaultMainContent = ` @@ -16,977 +16,689 @@ void main() { let mainContent = defaultMainContent; function hash(input) { - return crypto.createHash("md5").update(input).digest("hex"); + return crypto.createHash('md5').update(input).digest('hex'); } async function addCodeSnippetToEnv(mdFile, isSwiftEnabled) { - if ( - mdFile.includes("/v2/change_me/") || - mdFile.includes("/v2/contribute/") || - mdFile.includes("/v2/nodejs") || - mdFile.includes("/v2/golang") || - mdFile.includes("/v2/python") || - mdFile.includes("/v2/auth-react") || - mdFile.includes("/v2/website") || - mdFile.includes("/v2/react-native") || - mdFile.includes("/codeTypeChecking/") - ) { - return; - } - return new Promise((res, rej) => { - fs.readFile(mdFile, "utf8", async (err, data) => { - if (err) { - return rej(err); - } - let fileNameCounter = 0; - let originalData = data; - - let lines = originalData.split("\n"); - - let currentCodeSnippet = ""; - let currentCodeLanguage = ""; - let startAppendingToCodeSnippet = false; - for (let i = 0; i < lines.length; i++) { - let currLineTrimmed = lines[i].trim(); - if (startAppendingToCodeSnippet && !currLineTrimmed.startsWith("```")) { - currentCodeSnippet = currentCodeSnippet + "\n" + lines[i]; - } - if (currLineTrimmed.startsWith("```")) { - startAppendingToCodeSnippet = !startAppendingToCodeSnippet; - if (!startAppendingToCodeSnippet) { - if (currLineTrimmed !== "```") { - return rej( - new Error( - `Something wrong in how a code snippet has ended in ${mdFile}` - ) - ); - } - // we just finished copying a code snippet - if (currentCodeLanguage !== "ignore") { - await addCodeSnippetToEnvHelper( - currentCodeSnippet, - currentCodeLanguage, - mdFile, - fileNameCounter - ); - fileNameCounter++; + if (mdFile.includes("/v2/change_me/") || mdFile.includes("/v2/contribute/") || + mdFile.includes("/v2/nodejs") || mdFile.includes("/v2/golang") || mdFile.includes("/v2/python") || + mdFile.includes("/v2/auth-react") || mdFile.includes("/v2/website") || mdFile.includes("/v2/react-native") || mdFile.includes("/codeTypeChecking/")) { + return; + } + return new Promise((res, rej) => { + fs.readFile(mdFile, 'utf8', async (err, data) => { + if (err) { + return rej(err); } - currentCodeSnippet = ""; - currentCodeLanguage = ""; - } else { - // we are starting a code block - if ( - currLineTrimmed === "```js" || - currLineTrimmed.startsWith("```js ") || - currLineTrimmed === "```jsx" || - currLineTrimmed.startsWith("```jsx ") - ) { - return rej( - new Error( - `Please do not use js or jsx in code snippets. Only ts or tsx. Error in ` + - mdFile - ) - ); - } else if ( - currLineTrimmed === "```ts" || - currLineTrimmed.startsWith("```ts ") || - currLineTrimmed === "```tsx" || - currLineTrimmed.startsWith("```tsx ") - ) { - currentCodeLanguage = "typescript"; - } else if ( - currLineTrimmed === "```go" || - currLineTrimmed.startsWith("```go ") - ) { - currentCodeLanguage = "go"; - } else if ( - currLineTrimmed === "```python" || - currLineTrimmed.startsWith("```python ") - ) { - currentCodeLanguage = "python"; - } else if ( - currLineTrimmed === "```kotlin" || - currLineTrimmed.startsWith("```kotlin ") - ) { - currentCodeLanguage = "kotlin"; - } else if ( - currLineTrimmed === "```swift" || - currLineTrimmed.startsWith("```swift ") - ) { - currentCodeLanguage = isSwiftEnabled ? "swift" : "ignore"; - } else if ( - currLineTrimmed.includes("bash") || - currLineTrimmed.includes("yaml") || - currLineTrimmed.includes("cql") || - currLineTrimmed.includes("sql") || - currLineTrimmed.includes("batch") || - currLineTrimmed.includes("text") || - currLineTrimmed.includes("json") || - currLineTrimmed.includes("html") - ) { - currentCodeLanguage = "ignore"; - } else if ( - currLineTrimmed === "```dart" || - currLineTrimmed.startsWith("```dart ") - ) { - currentCodeLanguage = "dart"; - } else { - return rej( - new Error( - `UNABLE TO RECOGNISE LANGUAGE ${currLineTrimmed} in file ${mdFile}.` - ) - ); + let fileNameCounter = 0; + let originalData = data; + + let lines = originalData.split("\n"); + + let currentCodeSnippet = ""; + let currentCodeLanguage = ""; + let startAppendingToCodeSnippet = false; + for (let i = 0; i < lines.length; i++) { + let currLineTrimmed = lines[i].trim(); + if (startAppendingToCodeSnippet && !currLineTrimmed.startsWith("```")) { + currentCodeSnippet = currentCodeSnippet + "\n" + lines[i]; + } + if (currLineTrimmed.startsWith("```")) { + startAppendingToCodeSnippet = !startAppendingToCodeSnippet; + if (!startAppendingToCodeSnippet) { + if (currLineTrimmed !== "```") { + return rej(new Error(`Something wrong in how a code snippet has ended in ${mdFile}`)); + } + // we just finished copying a code snippet + if (currentCodeLanguage !== "ignore") { + await addCodeSnippetToEnvHelper(currentCodeSnippet, currentCodeLanguage, mdFile, fileNameCounter); + fileNameCounter++; + } + currentCodeSnippet = ""; + currentCodeLanguage = ""; + } else { + // we are starting a code block + if (currLineTrimmed === "```js" || currLineTrimmed.startsWith("```js ") || + currLineTrimmed === "```jsx" || currLineTrimmed.startsWith("```jsx ")) { + return rej(new Error(`Please do not use js or jsx in code snippets. Only ts or tsx. Error in ` + mdFile)); + } else if (currLineTrimmed === "```ts" || currLineTrimmed.startsWith("```ts ") || + currLineTrimmed === "```tsx" || currLineTrimmed.startsWith("```tsx ")) { + currentCodeLanguage = "typescript"; + } else if (currLineTrimmed === "```go" || currLineTrimmed.startsWith("```go ")) { + currentCodeLanguage = "go"; + } else if (currLineTrimmed === "```python" || currLineTrimmed.startsWith("```python ")) { + currentCodeLanguage = "python"; + } else if (currLineTrimmed === "```kotlin" || currLineTrimmed.startsWith("```kotlin ")) { + currentCodeLanguage = "kotlin"; + } else if (currLineTrimmed === "```swift" || currLineTrimmed.startsWith("```swift ")) { + currentCodeLanguage = isSwiftEnabled ? "swift" : "ignore" + } else if (currLineTrimmed.includes("bash") || currLineTrimmed.includes("yaml") || currLineTrimmed.includes("cql") || currLineTrimmed.includes("sql") || currLineTrimmed.includes("batch") || + currLineTrimmed.includes("text") || currLineTrimmed.includes("json") + || currLineTrimmed.includes("html")) { + currentCodeLanguage = "ignore" + } else if (currLineTrimmed === "```dart" || currLineTrimmed.startsWith("```dart ")) { + currentCodeLanguage = "dart"; + } else { + return rej(new Error(`UNABLE TO RECOGNISE LANGUAGE ${currLineTrimmed} in file ${mdFile}.`)); + } + } + } } - } - } - } - res(); + res(); + }); }); - }); } async function getFiles(dir) { - if (!fs.existsSync(dir)) { - return []; - } - const dirents = await readdir(dir, { withFileTypes: true }); - const files = await Promise.all( - dirents.map((dirent) => { - const res = path.resolve(dir, dirent.name); - return dirent.isDirectory() ? getFiles(res) : res; - }) - ); - return Array.prototype.concat(...files); + if (!fs.existsSync(dir)) { + return []; + } + const dirents = await readdir(dir, { withFileTypes: true }); + const files = await Promise.all(dirents.map((dirent) => { + const res = path.resolve(dir, dirent.name); + return dirent.isDirectory() ? getFiles(res) : res; + })); + return Array.prototype.concat(...files); } function cleanEmptyFoldersRecursively(folder) { - if (!fs.existsSync(folder)) { - return; - } - var isDir = fs.statSync(folder).isDirectory(); - if (!isDir) { - return; - } - var files = fs.readdirSync(folder); - if (files.length > 0) { - files.forEach(function (file) { - var fullPath = path.join(folder, file); - cleanEmptyFoldersRecursively(fullPath); - }); - - // re-evaluate files; after deleting subfolder - // we may have parent folder empty now - files = fs.readdirSync(folder); - } + if (!fs.existsSync(folder)) { + return; + } + var isDir = fs.statSync(folder).isDirectory(); + if (!isDir) { + return; + } + var files = fs.readdirSync(folder); + if (files.length > 0) { + files.forEach(function (file) { + var fullPath = path.join(folder, file); + cleanEmptyFoldersRecursively(fullPath); + }); + + // re-evaluate files; after deleting subfolder + // we may have parent folder empty now + files = fs.readdirSync(folder); + } - if (files.length == 0) { - fs.rmdirSync(folder); - return; - } + if (files.length == 0) { + fs.rmdirSync(folder); + return; + } } async function deleteFilesWithoutErrorsTypescript(stdout) { - let errors = stdout.split("\n"); - let fileNames = []; - - /** - * NOTE that because of how typescript reports file paths, all paths in - * fileNames will start from /snippets and not from the root (~/) - */ - errors.forEach((error) => { - // tsc output has some other lines + info that we are not interested in - if (error !== "" && error.includes("snippets")) { - fileNames.push(error.split("(")[0]); - } - }); - - let snippetsPathPrefix = "src/plugins/codeTypeChecking/jsEnv/snippets/"; - let files = await getFiles(snippetsPathPrefix); - await getRootDir(); - - files.forEach((file) => { - let actualPath = file.replace(rootDir, ""); - - // We replace the path from project root to snippets with just snippets/ to match the format that tsc outputs - if ( - !fileNames.includes(actualPath.replace(snippetsPathPrefix, "snippets/")) - ) { - let exists = fs.existsSync(actualPath); - if (exists) { - fs.rmSync(actualPath); - } - } - }); + let errors = stdout.split("\n"); + let fileNames = []; + + /** + * NOTE that because of how typescript reports file paths, all paths in + * fileNames will start from /snippets and not from the root (~/) + */ + errors.forEach(error => { + // tsc output has some other lines + info that we are not interested in + if (error !== "" && error.includes("snippets")) { + fileNames.push(error.split("(")[0]); + } + }) + + let snippetsPathPrefix = "src/plugins/codeTypeChecking/jsEnv/snippets/"; + let files = await getFiles(snippetsPathPrefix); + await getRootDir(); - cleanEmptyFoldersRecursively("src/plugins/codeTypeChecking/jsEnv/snippets"); + files.forEach(file => { + let actualPath = file.replace(rootDir, ""); + + // We replace the path from project root to snippets with just snippets/ to match the format that tsc outputs + if (!fileNames.includes(actualPath.replace(snippetsPathPrefix, "snippets/"))) { + let exists = fs.existsSync(actualPath); + if (exists) { + fs.rmSync(actualPath); + } + } + }); + + cleanEmptyFoldersRecursively("src/plugins/codeTypeChecking/jsEnv/snippets"); } + async function deleteFilesWithoutErrorsPython(stdout) { - let errors = stdout.split("\n"); - let fileNames = []; + let errors = stdout.split("\n"); + let fileNames = []; - errors.forEach((error) => { - if (error !== "" && error.includes("snippets")) { - fileNames.push(error.split(":")[0].trim()); - } - }); - - let snippetsPathPrefix = "src/plugins/codeTypeChecking/pythonEnv/snippets/"; - let files = await getFiles(snippetsPathPrefix); - await getRootDir(); - - files.forEach((file) => { - if (!fileNames.includes(file)) { - let exists = fs.existsSync(file); - if (exists) { - fs.rmSync(file); - } - } - }); + errors.forEach(error => { + if (error !== "" && error.includes("snippets")) { + fileNames.push(error.split(":")[0].trim()); + } + }) + + let snippetsPathPrefix = "src/plugins/codeTypeChecking/pythonEnv/snippets/"; + let files = await getFiles(snippetsPathPrefix); + await getRootDir(); - cleanEmptyFoldersRecursively( - "src/plugins/codeTypeChecking/pythonEnv/snippets" - ); + files.forEach(file => { + if (!fileNames.includes(file)) { + let exists = fs.existsSync(file); + if (exists) { + fs.rmSync(file); + } + } + }); + + cleanEmptyFoldersRecursively("src/plugins/codeTypeChecking/pythonEnv/snippets"); } async function checkCodeSnippets(language) { - // typescript.. - if (language === "typescript") { - await new Promise((res, rej) => { - exec( - "cd src/plugins/codeTypeChecking/jsEnv/ && npm run test", - async function (err, stdout, stderr) { - await deleteFilesWithoutErrorsTypescript(stdout); - if (err) { - console.log("\x1b[31m%s\x1b[0m", stdout); - console.log("\x1b[31m%s\x1b[0m", err); - console.log("=======SETUP INSTRS========\n"); - console.log( - "\x1b[36m%s\x1b[0m", - `To setup a JS env, run the following (from v2 folder): + // typescript.. + if (language === "typescript") { + await new Promise((res, rej) => { + exec("cd src/plugins/codeTypeChecking/jsEnv/ && npm run test", async function (err, stdout, stderr) { + await deleteFilesWithoutErrorsTypescript(stdout); + if (err) { + console.log('\x1b[31m%s\x1b[0m', stdout); + console.log('\x1b[31m%s\x1b[0m', err); + console.log("=======SETUP INSTRS========\n"); + console.log('\x1b[36m%s\x1b[0m', `To setup a JS env, run the following (from v2 folder): - cd src/plugins/codeTypeChecking/jsEnv/ - npm i - - npm run test (to make sure that it's setup correctly)` - ); - console.log("==========================\n"); + - npm run test (to make sure that it's setup correctly)`) + console.log("==========================\n"); - return rej(err); - } - res(); - } - ); - }); - } else if (language === "go") { - await new Promise((res, rej) => { - exec( - "cd src/plugins/codeTypeChecking/goEnv/ && go mod tidy && go build ./...", - function (err, stdout, stderr) { - if (err) { - console.log("\x1b[31m%s\x1b[0m", stdout); - console.log("\x1b[31m%s\x1b[0m", err); - console.log("=======SETUP INSTRS========\n"); - console.log( - "\x1b[36m%s\x1b[0m", - `Make sure that you have go installed on your system and try this command again` - ); - console.log("==========================\n"); - return rej(err); - } - res(); - } - ); - }); - } else if (language === "python") { - await new Promise((res, rej) => { - exec( - "cd src/plugins/codeTypeChecking/pythonEnv/ && ./checkSnippets.sh", - async function (err, stdout, stderr) { - await deleteFilesWithoutErrorsPython(stdout); - if (err) { - console.log("\x1b[31m%s\x1b[0m", stdout); - console.log("\x1b[31m%s\x1b[0m", err); - console.log("=======SETUP INSTRS========\n"); - console.log( - "\x1b[36m%s\x1b[0m", - `To setup a python env, run the following (from v2 folder): + return rej(err); + } + res(); + }); + }) + } else if (language === "go") { + await new Promise((res, rej) => { + exec("cd src/plugins/codeTypeChecking/goEnv/ && go mod tidy && go build ./...", function (err, stdout, stderr) { + if (err) { + console.log('\x1b[31m%s\x1b[0m', stdout); + console.log('\x1b[31m%s\x1b[0m', err); + console.log("=======SETUP INSTRS========\n"); + console.log('\x1b[36m%s\x1b[0m', `Make sure that you have go installed on your system and try this command again`) + console.log("==========================\n"); + return rej(err); + } + res(); + }); + }) + } else if (language === "python") { + await new Promise((res, rej) => { + exec("cd src/plugins/codeTypeChecking/pythonEnv/ && ./checkSnippets.sh", async function (err, stdout, stderr) { + await deleteFilesWithoutErrorsPython(stdout); + if (err) { + console.log('\x1b[31m%s\x1b[0m', stdout); + console.log('\x1b[31m%s\x1b[0m', err); + console.log("=======SETUP INSTRS========\n"); + console.log('\x1b[36m%s\x1b[0m', `To setup a python env, run the following (from v2 folder): - cd src/plugins/codeTypeChecking/pythonEnv/ - virtualenv ./venv - source venv/bin/activate - pip install -r requirements.txt - - pylint ./snippets (to make sure that it's setup correctly)` - ); - console.log("==========================\n"); - return rej(err); - } - res(); - } - ); - }); - } else if (language === "kotlin") { - await new Promise((res, rej) => { - exec( - "cd src/plugins/codeTypeChecking/kotlinEnv/ && ./gradlew build", - async function (err, stdout, stderr) { - if (err) { - console.log("\x1b[31m%s\x1b[0m", stdout); - console.log("\x1b[31m%s\x1b[0m", err); - console.log("=======SETUP INSTRS========\n"); - console.log( - "\x1b[36m%s\x1b[0m", - `Make sure that you have kotlin installed on your system and try this command again` - ); - console.log("==========================\n"); - return rej(err); - } - res(); - } - ); - }); - } else if (language === "swift") { - await new Promise((res, rej) => { - exec( - "cd src/plugins/codeTypeChecking/iosenv/ && set -o pipefail && xcodebuild -workspace iosenv.xcworkspace -scheme iosenv build CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO -quiet | ./Pods/xcbeautify/xcbeautify", - async function (err, stdout, stderr) { - if (err) { - console.log("\x1b[31m%s\x1b[0m", stdout); - console.log("\x1b[31m%s\x1b[0m", err); - console.log("=======SETUP INSTRS========\n"); - console.log( - "\x1b[36m%s\x1b[0m", - `Make sure that you have Xcode installed on your system and try this command again` - ); - console.log( - "\x1b[36m%s\x1b[0m", - `Make sure that you have run 'pod install' inside iosenv and try this command again` - ); - console.log("==========================\n"); - return rej(err); - } - res(); - } - ); - }); - } else if (language === "dart") { - await new Promise((res, rej) => { - exec( - "cd src/plugins/codeTypeChecking/dart_env/ && flutter build web -t lib/snippets/main.dart", - async function (err, stdout, stderr) { - if (err) { - console.log("\x1b[31m%s\x1b[0m", stdout); - console.log("\x1b[31m%s\x1b[0m", err); - console.log("=======SETUP INSTRS========\n"); - console.log( - "\x1b[36m%s\x1b[0m", - `Make sure that you have flutter setup on your system and try this command again` - ); - console.log( - "\x1b[36m%s\x1b[0m", - `Make sure that you have run 'flutter pub get' inside dart_env and try this command again` - ); - console.log("==========================\n"); - return rej(err); - } - res(); - } - ); - }); - } else { - throw new Error("Unsupported language in checkCodeSnippets"); - } + - pylint ./snippets (to make sure that it's setup correctly)`) + console.log("==========================\n"); + return rej(err); + } + res(); + }); + }) + } else if (language === "kotlin") { + await new Promise((res, rej) => { + exec("cd src/plugins/codeTypeChecking/kotlinEnv/ && ./gradlew build", async function (err, stdout, stderr) { + if (err) { + console.log('\x1b[31m%s\x1b[0m', stdout); + console.log('\x1b[31m%s\x1b[0m', err); + console.log("=======SETUP INSTRS========\n"); + console.log('\x1b[36m%s\x1b[0m', `Make sure that you have kotlin installed on your system and try this command again`) + console.log("==========================\n"); + return rej(err); + } + res(); + }); + }) + } else if (language === "swift") { + await new Promise((res, rej) => { + exec("cd src/plugins/codeTypeChecking/iosenv/ && set -o pipefail && xcodebuild -workspace iosenv.xcworkspace -scheme iosenv build CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO -quiet | ./Pods/xcbeautify/xcbeautify", async function (err, stdout, stderr) { + if (err) { + console.log('\x1b[31m%s\x1b[0m', stdout); + console.log('\x1b[31m%s\x1b[0m', err); + console.log("=======SETUP INSTRS========\n"); + console.log('\x1b[36m%s\x1b[0m', `Make sure that you have Xcode installed on your system and try this command again`) + console.log('\x1b[36m%s\x1b[0m', `Make sure that you have run 'pod install' inside iosenv and try this command again`) + console.log("==========================\n"); + return rej(err); + } + res(); + }); + }) + } else if (language === "dart") { + await new Promise((res, rej) => { + exec("cd src/plugins/codeTypeChecking/dart_env/ && flutter build web -t lib/snippets/main.dart", async function (err, stdout, stderr) { + if (err) { + console.log('\x1b[31m%s\x1b[0m', stdout); + console.log('\x1b[31m%s\x1b[0m', err); + console.log("=======SETUP INSTRS========\n"); + console.log('\x1b[36m%s\x1b[0m', `Make sure that you have flutter setup on your system and try this command again`) + console.log('\x1b[36m%s\x1b[0m', `Make sure that you have run 'flutter pub get' inside dart_env and try this command again`) + console.log("==========================\n"); + return rej(err); + } + res(); + }); + }) + } else { + throw new Error("Unsupported language in checkCodeSnippets"); + } } let rootDir = undefined; // uptil v2 async function getRootDir() { - if (rootDir === undefined) { - rootDir = await new Promise((res) => - exec("pwd", function (err, stdout, stderr) { - res(stdout); - }) - ); - rootDir = rootDir.trim() + "/"; - // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ - } - - return rootDir; + if (rootDir === undefined) { + rootDir = await new Promise(res => exec("pwd", function (err, stdout, stderr) { + res(stdout); + })); + rootDir = rootDir.trim() + "/"; + // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ + } + + return rootDir; } async function getRecipeName(mdFile) { - if (rootDir === undefined) { - rootDir = await new Promise((res) => - exec("pwd", function (err, stdout, stderr) { - res(stdout); - }) - ); - rootDir = rootDir.trim() + "/"; - // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ - } - let postV2 = mdFile.replace(rootDir, ""); - return postV2.split("/")[0]; + if (rootDir === undefined) { + rootDir = await new Promise(res => exec("pwd", function (err, stdout, stderr) { + res(stdout); + })); + rootDir = rootDir.trim() + "/"; + // at this point, rootDir is /Users/.../main-website/docs/v2/ or if only cloned docs, then its /Users/.../docs/v2/ + } + let postV2 = mdFile.replace(rootDir, ""); + return postV2.split("/")[0]; } -async function addCodeSnippetToEnvHelper( - codeSnippet, - language, - mdFile, - codeBlockCountInFile -) { - // we replace all the variables here so that the code can compile: - - // Excluding __OMIT_START__ and __OMIT_END__ tags from the code snippet before running the type check plugin - // We need to do this before intializing ogCodeSnippet to prevent false warnings about not using docs variables - codeSnippet = codeSnippet.replace( - /__OMIT_START__([\s\S]*?)__OMIT_END__/g, - "$1" - ); - - let ogCodeSnippet = codeSnippet; - codeSnippet = codeSnippet.replaceAll( - "^{coreInjector_connection_uri_comment}", - "" - ); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_uri}", '"",'); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key_commented}", ""); - codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key}", '""'); - codeSnippet = codeSnippet.replaceAll( - "^{coreInjector_connection_uri_comment_with_hash}", - "" - ); - codeSnippet = codeSnippet.replaceAll( - "^{coreInjector_api_key_commented_with_hash}", - "" - ); - - codeSnippet = codeSnippet.replaceAll( - "^{form_flowType}", - "USER_INPUT_CODE_AND_MAGIC_LINK" - ); - codeSnippet = codeSnippet.replaceAll("^{form_contactMethod}", "PHONE"); - codeSnippet = codeSnippet.replaceAll( - "^{form_contactMethod_initialize_Python}", - "ContactPhoneOnlyConfig" - ); - codeSnippet = codeSnippet.replaceAll( - "^{form_contactMethod_import_Python}", - "from supertokens_python.recipe.passwordless import ContactPhoneOnlyConfig" - ); - codeSnippet = codeSnippet.replaceAll( - "^{form_contactMethod_sendCB_Go}", - `ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ +async function addCodeSnippetToEnvHelper(codeSnippet, language, mdFile, codeBlockCountInFile) { + // we replace all the variables here so that the code can compile: + + // Excluding __OMIT_START__ and __OMIT_END__ tags from the code snippet before running the type check plugin + // We need to do this before intializing ogCodeSnippet to prevent false warnings about not using docs variables + codeSnippet = codeSnippet.replace(/__OMIT_START__([\s\S]*?)__OMIT_END__/g, '$1'); + + let ogCodeSnippet = codeSnippet; + codeSnippet = codeSnippet.replaceAll("^{coreInjector_connection_uri_comment}", ""); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_uri}", "\"\","); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key_commented}", ""); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key}", "\"\""); + codeSnippet = codeSnippet.replaceAll("^{coreInjector_connection_uri_comment_with_hash}", "") + codeSnippet = codeSnippet.replaceAll("^{coreInjector_api_key_commented_with_hash}", "") + + codeSnippet = codeSnippet.replaceAll("^{form_flowType}", "USER_INPUT_CODE_AND_MAGIC_LINK"); + codeSnippet = codeSnippet.replaceAll("^{form_contactMethod}", "PHONE"); + codeSnippet = codeSnippet.replaceAll("^{form_contactMethod_initialize_Python}", "ContactPhoneOnlyConfig"); + codeSnippet = codeSnippet.replaceAll("^{form_contactMethod_import_Python}", "from supertokens_python.recipe.passwordless import ContactPhoneOnlyConfig"); + codeSnippet = codeSnippet.replaceAll("^{form_contactMethod_sendCB_Go}", + `ContactMethodPhone: plessmodels.ContactMethodPhoneConfig{ Enabled: true, -},` - ); - - let recipeName = await getRecipeName(mdFile); - let replaceMap = { ...mdVars["common"], ...mdVars[recipeName] }; - if (replaceMap !== undefined) { - let keys = Object.keys(replaceMap); - for (let i = 0; i < keys.length; i++) { - if (codeSnippet.includes(`^{${keys[i]}}`)) { - codeSnippet = codeSnippet.replaceAll( - `^{${keys[i]}}`, - replaceMap[keys[i]] - ); - } - } - } - - // this block is there so that the dev knows that the resulting block should use variable code snippets. - if (ogCodeSnippet !== codeSnippet) { - for (let i = 0; i < 50; i++) { - if ( - language === "typescript" || - language === "go" || - language === "kotlin" || - language === "swift" || - language === "dart" - ) { - codeSnippet = - "// THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + - codeSnippet; - } else if (language === "python") { - codeSnippet = - "# THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + - codeSnippet; - } else { - throw new Error("language not supported"); - } - } - } - - if (language === "typescript") { - if (codeSnippet.includes("require(")) { - // except for the attack protection suite , where we need to allow require for compatibility reasons, - // as the SDK URL is dynamic and import might break some builds - if (!codeSnippet.includes('require("https://deviceid.supertokens.io')) { - throw new Error("Do not use 'require' in TS code. Error in " + mdFile); +},`); + + let recipeName = await getRecipeName(mdFile); + let replaceMap = { ...mdVars["common"], ...mdVars[recipeName] }; + if (replaceMap !== undefined) { + let keys = Object.keys(replaceMap); + for (let i = 0; i < keys.length; i++) { + if (codeSnippet.includes(`^{${keys[i]}}`)) { + codeSnippet = codeSnippet.replaceAll(`^{${keys[i]}}`, replaceMap[keys[i]]); + } } - } } - codeSnippet = `export { }\n// Original: ${mdFile}\n${codeSnippet}`; // see https://www.aritsltd.com/blog/frontend-development/cannot-redeclare-block-scoped-variable-the-reason-behind-the-error-and-the-way-to-resolve-it/ - // vue requires use of ", ""); + // this block is there so that the dev knows that the resulting block should use variable code snippets. + if (ogCodeSnippet !== codeSnippet) { + for (let i = 0; i < 50; i++) { + if (language === "typescript" || language === "go" || language === "kotlin" || language === "swift" || language === "dart") { + codeSnippet = "// THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + codeSnippet; + } else if (language === "python") { + codeSnippet = "# THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE\n" + codeSnippet; + } else { + throw new Error("language not supported"); + } + } } - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/jsEnv/snippets/" + folderName, - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake( - "src/plugins/codeTypeChecking/jsEnv/snippets/" + - folderName + - "/index.tsx", - codeSnippet - ); - fs.writeFile( - "src/plugins/codeTypeChecking/jsEnv/snippets/" + - folderName + - "/index.tsx", - codeSnippet, - function (err) { + + if (language === "typescript") { + if (codeSnippet.includes("require(")) { + // except for the attack protection suite , where we need to allow require for compatibility reasons, + // as the SDK URL is dynamic and import might break some builds + if (!codeSnippet.includes('require("https://deviceid.supertokens.io')) { + throw new Error("Do not use 'require' in TS code. Error in " + mdFile); + } + } + codeSnippet = `export { }\n// Original: ${mdFile}\n${codeSnippet}`; // see https://www.aritsltd.com/blog/frontend-development/cannot-redeclare-block-scoped-variable-the-reason-behind-the-error-and-the-way-to-resolve-it/ + + // vue requires use of ", ""); + } + + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + await new Promise(async (res, rej) => { + fs.mkdir('src/plugins/codeTypeChecking/jsEnv/snippets/' + folderName, { recursive: true }, async (err) => { if (err) { - rej(err); + rej(err); } else { - res(); + await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/jsEnv/snippets/' + folderName + "/index.tsx", codeSnippet); + fs.writeFile('src/plugins/codeTypeChecking/jsEnv/snippets/' + folderName + "/index.tsx", codeSnippet, function (err) { + if (err) { + rej(err); + } else { + res(); + } + }); } - } - ); - } + }); + }); + } else if (language === "go") { + if (codeSnippet.includes("/supertokens-go/") || codeSnippet.includes("/supertokens-go\n")) { + throw new Error("Do not use supertokens-go package. Use supertokens-golang package. Error in " + mdFile); } - ); - }); - } else if (language === "go") { - if ( - codeSnippet.includes("/supertokens-go/") || - codeSnippet.includes("/supertokens-go\n") - ) { - throw new Error( - "Do not use supertokens-go package. Use supertokens-golang package. Error in " + - mdFile - ); - } - // we change the last folder path dir to be a valid go module name - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - let splittedFolder = folderName.split("/"); - let lastDir = splittedFolder[splittedFolder.length - 1]; - lastDir = lastDir.replaceAll("-", "").replaceAll(".", ""); - splittedFolder[splittedFolder.length - 1] = lastDir; - let newFolderName = splittedFolder.join("/"); - - // adding package on top of go file - codeSnippet = `package ${lastDir}\n/*\n${mdFile}\n*/\n${codeSnippet}`; - - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/goEnv/snippets/" + newFolderName, - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake( - "src/plugins/codeTypeChecking/goEnv/snippets/" + - newFolderName + - "/main.go", - codeSnippet - ); - fs.writeFile( - "src/plugins/codeTypeChecking/goEnv/snippets/" + - newFolderName + - "/main.go", - codeSnippet, - function (err) { + // we change the last folder path dir to be a valid go module name + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + let splittedFolder = folderName.split("/"); + let lastDir = splittedFolder[splittedFolder.length - 1]; + lastDir = lastDir.replaceAll("-", "").replaceAll(".", ""); + splittedFolder[splittedFolder.length - 1] = lastDir; + let newFolderName = splittedFolder.join("/"); + + // adding package on top of go file + codeSnippet = `package ${lastDir}\n/*\n${mdFile}\n*/\n${codeSnippet}`; + + await new Promise(async (res, rej) => { + fs.mkdir('src/plugins/codeTypeChecking/goEnv/snippets/' + newFolderName, { recursive: true }, async (err) => { if (err) { - rej(err); + rej(err); } else { - res(); + await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/goEnv/snippets/' + newFolderName + "/main.go", codeSnippet); + fs.writeFile('src/plugins/codeTypeChecking/goEnv/snippets/' + newFolderName + "/main.go", codeSnippet, function (err) { + if (err) { + rej(err); + } else { + res(); + } + }); } - } - ); - } - } - ); - }); - } else if (language === "python") { - codeSnippet = `# ${mdFile}\n${codeSnippet}`; - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/pythonEnv/snippets/" + folderName, - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake( - "src/plugins/codeTypeChecking/pythonEnv/snippets/" + - folderName + - "/main.py", - codeSnippet - ); - fs.writeFile( - "src/plugins/codeTypeChecking/pythonEnv/snippets/" + - folderName + - "/main.py", - codeSnippet, - function (err) { + }); + }); + } else if (language === "python") { + codeSnippet = `# ${mdFile}\n${codeSnippet}` + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + await new Promise(async (res, rej) => { + fs.mkdir('src/plugins/codeTypeChecking/pythonEnv/snippets/' + folderName, { recursive: true }, async (err) => { if (err) { - rej(err); + rej(err); } else { - res(); + await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/pythonEnv/snippets/' + folderName + "/main.py", codeSnippet); + fs.writeFile('src/plugins/codeTypeChecking/pythonEnv/snippets/' + folderName + "/main.py", codeSnippet, function (err) { + if (err) { + rej(err); + } else { + res(); + } + }); } - } - ); - } - } - ); - }); - } else if (language === "kotlin") { - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - folderName = folderName.replace(".mdx", ""); - let packageNameSplitted = folderName.split("/"); - packageNameSplitted = packageNameSplitted.map((i) => { - if (i.includes("-")) { - return "`" + i + "`"; - } - return i; - }); - codeSnippet = `package com.example.myapplication${packageNameSplitted.join( - "." - )}\n\n// Original: ${mdFile}\n${codeSnippet}`; - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/" + - folderName, - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake( - "src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/" + - folderName + - "/Code.kt", - codeSnippet - ); - fs.writeFile( - "src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/" + - folderName + - "/Code.kt", - codeSnippet, - function (err) { + }); + }); + } else if (language === "kotlin") { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + folderName = folderName.replace(".mdx", ""); + let packageNameSplitted = folderName.split("/"); + packageNameSplitted = packageNameSplitted.map(i => { + if (i.includes("-")) { + return "`" + i + "`" + } + return i; + }) + codeSnippet = `package com.example.myapplication${packageNameSplitted.join(".")}\n\n// Original: ${mdFile}\n${codeSnippet}`; + await new Promise(async (res, rej) => { + fs.mkdir('src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/' + folderName, { recursive: true }, async (err) => { if (err) { - rej(err); + rej(err); } else { - res(); + await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/' + folderName + "/Code.kt", codeSnippet); + fs.writeFile('src/plugins/codeTypeChecking/kotlinEnv/app/src/main/java/com/example/myapplication/' + folderName + "/Code.kt", codeSnippet, function (err) { + if (err) { + rej(err); + } else { + res(); + } + }); } - } - ); - } - } - ); - }); - } else if (language === "swift") { - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - folderName = folderName.replace(".mdx", ""); - let packageNameSplitted = folderName.split("/"); - packageNameSplitted = packageNameSplitted.map((i) => { - if (i.includes("-")) { - return "`" + i + "`"; - } - return i; - }); - codeSnippet = `// Original: ${mdFile}\n${codeSnippet}`; - const folderNameSplit = folderName.split("/"); - const lastFolderPath = - folderNameSplit[folderNameSplit.length - 1] + new Date().getTime(); - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/" + folderName, - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake( - "src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/" + - folderName + - "/Code.swift", - codeSnippet - ); - const filename = - "src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/" + - folderName + - `/${lastFolderPath}.swift`; - fs.writeFile(filename, codeSnippet, function (err) { - if (err) { - rej(err); - } else { - execSync( - `./src/plugins/codeTypeChecking/iosenv/createFile.rb "${filename}"` - ); - res(); - } }); - } - } - ); - }); - } else if (language === "dart") { - let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/dart_env/lib/snippets", - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - res(); - } - } - ); - }); - - await new Promise(async (res, rej) => { - fs.writeFile( - "src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", - mainContent, - async (err) => { - if (err) { - rej(err); - } else { - res(); - } - } - ); - }); + }); + } else if (language === "swift") { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; + folderName = folderName.replace(".mdx", ""); + let packageNameSplitted = folderName.split("/"); + packageNameSplitted = packageNameSplitted.map(i => { + if (i.includes("-")) { + return "`" + i + "`" + } + return i; + }) + codeSnippet = `// Original: ${mdFile}\n${codeSnippet}`; + const folderNameSplit = folderName.split("/"); + const lastFolderPath = folderNameSplit[folderNameSplit.length - 1] + new Date().getTime() + await new Promise(async (res, rej) => { + fs.mkdir('src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/' + folderName, { recursive: true }, async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/' + folderName + "/Code.swift", codeSnippet); + const filename = 'src/plugins/codeTypeChecking/iosenv/iosenv/Snippets/' + folderName + `/${lastFolderPath}.swift`; + fs.writeFile(filename, codeSnippet, function (err) { + if (err) { + rej(err); + } else { + execSync(`./src/plugins/codeTypeChecking/iosenv/createFile.rb "${filename}"`) + res(); + } + }); + } + }); + }); + } else if (language === "dart") { + let folderName = mdFile.replaceAll("~", "") + codeBlockCountInFile; - await new Promise(async (res, rej) => { - fs.mkdir( - "src/plugins/codeTypeChecking/dart_env/lib/snippets/" + folderName, - { recursive: true }, - async (err) => { - if (err) { - rej(err); - } else { - await assertThatUserIsNotRemovedDocsVariableByMistake( - "src/plugins/codeTypeChecking/dart_env/lib/snippets/" + - folderName + - "/index.dart", - codeSnippet - ); - fs.writeFile( - "src/plugins/codeTypeChecking/dart_env/lib/snippets/" + - folderName + - "/index.dart", - codeSnippet, - function (err) { + await new Promise(async (res, rej) => { + fs.mkdir("src/plugins/codeTypeChecking/dart_env/lib/snippets", { recursive: true }, async (err) => { if (err) { - rej(err); + rej(err); } else { - res(); + res(); } - } - ); - - let dartImportStatement = `import 'package:dart_env/snippets/${folderName}/index.dart';`; - mainContent = dartImportStatement + "\n" + mainContent; - - await new Promise(async (res, rej) => { - fs.writeFile( - "src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", - mainContent, - async (err) => { - if (err) { + }) + }); + + await new Promise(async (res, rej) => { + fs.writeFile("src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", mainContent, async (err) => { + if (err) { rej(err); - } else { + } else { res(); - } } - ); + }) + }); + + await new Promise(async (res, rej) => { + fs.mkdir('src/plugins/codeTypeChecking/dart_env/lib/snippets/' + folderName, { recursive: true }, async (err) => { + if (err) { + rej(err); + } else { + await assertThatUserIsNotRemovedDocsVariableByMistake('src/plugins/codeTypeChecking/dart_env/lib/snippets/' + folderName + "/index.dart", codeSnippet); + fs.writeFile('src/plugins/codeTypeChecking/dart_env/lib/snippets/' + folderName + "/index.dart", codeSnippet, function (err) { + if (err) { + rej(err); + } else { + res(); + } + }); + + let dartImportStatement = `import 'package:dart_env/snippets/${folderName}/index.dart';`; + mainContent = dartImportStatement + "\n" + mainContent; + + await new Promise(async (res, rej) => { + fs.writeFile("src/plugins/codeTypeChecking/dart_env/lib/snippets/main.dart", mainContent, async (err) => { + if (err) { + rej(err); + } else { + res(); + } + }) + }); + } }); - } - } - ); - }); - } else { - throw new Error("Unsupported language in addCodeSnippetToEnvHelper"); - } + }); + } else { + throw new Error("Unsupported language in addCodeSnippetToEnvHelper"); + } } -async function assertThatUserIsNotRemovedDocsVariableByMistake( - path, - codeSnippet -) { - /* first we try and read the contents to see if this has +async function assertThatUserIsNotRemovedDocsVariableByMistake(path, codeSnippet) { + /* first we try and read the contents to see if this has THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE and if the new code snippet doesn't then, the dev has made a mistake of removing variables...*/ - return new Promise((res, rej) => { - fs.readFile(path, "utf8", function (err, data) { - if (data !== undefined) { - if ( - data.includes( - "THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE" - ) && - !codeSnippet.includes( - "THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE" - ) - ) { - let message = - "DID YOU FORGET TO USE DOCS VARIABLES IN A RECENT CODE CHANGE? PLEASE CHECK" + - "\n\nIf you think this error is unrelated to your changes, try deleting the `snippets` folder for all languages and run again.\n\nThe file path is: " + - path; - return rej(new Error(message)); - } - } - res(); - }); - }); + return new Promise((res, rej) => { + fs.readFile(path, 'utf8', function (err, data) { + if (data !== undefined) { + if (data.includes("THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE") && !codeSnippet.includes("THIS FILE CONTAINS DOCS VARIABLES. PLEASE DO NOT FORGET TO USE THOSE")) { + let message = "DID YOU FORGET TO USE DOCS VARIABLES IN A RECENT CODE CHANGE? PLEASE CHECK" + + "\n\nIf you think this error is unrelated to your changes, try deleting the `snippets` folder for all languages and run again.\n\nThe file path is: " + path + return rej(new Error(message)); + } + } + res(); + }); + }) } function replaceCustomPlaceholdersInLine(child, exportedVariables) { - // A child will either have value properties or more children - - // If it has a value, check if it is using a variable. If it is then replace otherwise skip - if (child.value) { - var valueCopy = child.value; - - // Exclude sections marked by __OMIT_START__ and __OMIT_END__ from the code snippet, along with the tags themselves - valueCopy = valueCopy.replace(/__OMIT_START__[\s\S]*?__OMIT_END__\n?/g, ""); - - let eachLine = valueCopy.split("\n"); - let newLines = []; - for (let i = 0; i < eachLine.length; i++) { - let line = eachLine[i]; - // If the line contains ts-ignore or ends with a comment indicating it should be removed - if ( - line.includes("@ts-ignore") || - /(\/\/|#) typecheck-only, removed from output$/.test(line) - ) { - continue; - } - - /** - * For snippets that use an older version of supertokens-node we use supertokens-node7 to import - * If the import contains supertokens-node7 we replace it with supertokens-node for the final - * rendered snippet - */ - if (line.includes("supertokens-node7")) { - line = line.split("supertokens-node7").join("supertokens-node"); - newLines.push(line); - continue; - } - - /** - * For snippets that contain supertokensUIInit, we add the dic id param parameter - */ - if (line.includes("supertokensUIInit(")) { - line = line - .split("supertokensUIInit(") - .join('(window as any).supertokensUIInit("supertokensui", '); - newLines.push(line); - continue; - } - if ( - line.includes("supertokensUI") && - !line.includes("supertokens-auth-react-script") - ) { - line = line - .split("supertokensUI") - .join("(window as any).supertokensUI"); - newLines.push(line); - continue; - } - - /** - * For snippets that use v5 react-router-dom we use react-router-dom5 to import - * If the import contains react-router-dom5 we replace it with react-router-dom for the final - * rendered snippet - */ - if (line.includes("react-router-dom5")) { - line = line.split("react-router-dom5").join("react-router-dom"); - newLines.push(line); - continue; - } - - /** - * For python code snippets that contain "# type: ignore", we remove that - * string snippet from the line - */ - if (line.includes("# type: ignore")) { - line = line.split("# type: ignore").join(""); - newLines.push(line); - continue; - } - if (line.includes("#type: ignore")) { - line = line.split("#type: ignore").join(""); - newLines.push(line); - continue; - } - if (line.includes("# type:ignore")) { - line = line.split("# type:ignore").join(""); - newLines.push(line); - continue; - } - if (line.includes("#type:ignore")) { - line = line.split("#type:ignore").join(""); - newLines.push(line); - continue; - } - - // if the line contains "# pyright: ", then we remove that line - if (line.includes("# pyright:") || line.includes("#pyright:")) { - continue; - } - - /** - * For snippets that use supertokens-web-js as an HTML script we import supertokens-web-js-script and supertokens-auth-react-script for types. - * If the line contains this we skip adding the line - */ - if ( - line.includes("supertokens-web-js-script") || - line.includes("supertokens-auth-react-script") - ) { - continue; - } - - /** - * For iOS snippets we need to use fileprivate class to avoid build errors in Xcode due to duplicate declarations. - * This replaces the fileprivate declaration with a simple class declaration in the final output - */ - if (line.includes("fileprivate class")) { - line = line.split("fileprivate class").join("class"); - newLines.push(line); - continue; - } - - newLines.push(eachLine[i]); - } + // A child will either have value properties or more children + + // If it has a value, check if it is using a variable. If it is then replace otherwise skip + if (child.value) { + var valueCopy = child.value; + + // Exclude sections marked by __OMIT_START__ and __OMIT_END__ from the code snippet, along with the tags themselves + valueCopy = valueCopy.replace(/__OMIT_START__[\s\S]*?__OMIT_END__\n?/g, ''); + + let eachLine = valueCopy.split("\n"); + let newLines = []; + for (let i = 0; i < eachLine.length; i++) { + let line = eachLine[i]; + // If the line contains ts-ignore or ends with a comment indicating it should be removed + if (line.includes("@ts-ignore") || /(\/\/|#) typecheck-only, removed from output$/.test(line)) { + continue; + } - child.value = newLines.join("\n"); - } + /** + * For snippets that use an older version of supertokens-node we use supertokens-node7 to import + * If the import contains supertokens-node7 we replace it with supertokens-node for the final + * rendered snippet + */ + if (line.includes("supertokens-node7")) { + line = line.split("supertokens-node7").join("supertokens-node"); + newLines.push(line); + continue; + } - // If it has children then repeat recursively - if (child.children) { - child.children = child.children.map((subChild) => { - return replaceCustomPlaceholdersInLine(subChild, exportedVariables); - }); - } + /** + * For snippets that contain supertokensUIInit, we add the dic id param parameter + */ + if (line.includes("supertokensUIInit(")) { + line = line.split("supertokensUIInit(").join("(window as any).supertokensUIInit(\"supertokensui\", "); + newLines.push(line); + continue; + } + if (line.includes("supertokensUI") && !line.includes("supertokens-auth-react-script")) { + line = line.split("supertokensUI").join("(window as any).supertokensUI"); + newLines.push(line); + continue; + } + + /** + * For snippets that use v5 react-router-dom we use react-router-dom5 to import + * If the import contains react-router-dom5 we replace it with react-router-dom for the final + * rendered snippet + */ + if (line.includes("react-router-dom5")) { + line = line.split("react-router-dom5").join("react-router-dom"); + newLines.push(line); + continue; + } + + /** + * For python code snippets that contain "# type: ignore", we remove that + * string snippet from the line + */ + if (line.includes("# type: ignore")) { + line = line.split("# type: ignore").join(""); + newLines.push(line); + continue; + } + if (line.includes("#type: ignore")) { + line = line.split("#type: ignore").join(""); + newLines.push(line); + continue; + } + if (line.includes("# type:ignore")) { + line = line.split("# type:ignore").join(""); + newLines.push(line); + continue; + } + if (line.includes("#type:ignore")) { + line = line.split("#type:ignore").join(""); + newLines.push(line); + continue; + } + + // if the line contains "# pyright: ", then we remove that line + if (line.includes("# pyright:") || line.includes("#pyright:")) { + continue; + } + + /** + * For snippets that use supertokens-web-js as an HTML script we import supertokens-web-js-script and supertokens-auth-react-script for types. + * If the line contains this we skip adding the line + */ + if (line.includes("supertokens-web-js-script") || line.includes("supertokens-auth-react-script")) { + continue; + } + + /** + * For iOS snippets we need to use fileprivate class to avoid build errors in Xcode due to duplicate declarations. + * This replaces the fileprivate declaration with a simple class declaration in the final output + */ + if (line.includes("fileprivate class")) { + line = line.split("fileprivate class").join("class"); + newLines.push(line); + continue; + } + + newLines.push(eachLine[i]); + } + + child.value = newLines.join("\n"); + } + + // If it has children then repeat recursively + if (child.children) { + child.children = child.children.map(subChild => { + return replaceCustomPlaceholdersInLine(subChild, exportedVariables); + }) + } - return child; + return child; } -module.exports = { - addCodeSnippetToEnv, - checkCodeSnippets, - replaceCustomPlaceholdersInLine, -}; +module.exports = { addCodeSnippetToEnv, checkCodeSnippets, replaceCustomPlaceholdersInLine } \ No newline at end of file From d58c94fafe7b11a414cb9d5ac714e0ceaae569e0 Mon Sep 17 00:00:00 2001 From: Victor Bojica Date: Tue, 15 Oct 2024 18:23:32 +0300 Subject: [PATCH 3/4] cleanup frontend SDK snippet note --- v2/attackprotectionsuite/frontend-setup.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/attackprotectionsuite/frontend-setup.mdx b/v2/attackprotectionsuite/frontend-setup.mdx index 285c98de5..df52de07e 100644 --- a/v2/attackprotectionsuite/frontend-setup.mdx +++ b/v2/attackprotectionsuite/frontend-setup.mdx @@ -22,7 +22,7 @@ Below is an example of how to implement request ID generation on your frontend: ```tsx const ENVIRONMENT_ID = ""; // Your environment ID that you received from the SuperTokens team // Initialize the agent on page load using your public API key that you received from the SuperTokens team. -const supertokensRequestIdPromise = require("https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s?apiKey=") +const supertokensRequestIdPromise = require("https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s?apiKey=") .then((RequestId: any) => RequestId.load({ endpoint: [ 'https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/CnsdzKsyFKU8Q3h2', @@ -42,7 +42,7 @@ async function getRequestId() { ``` :::note -If you are using a build system such as Webpack, make sure that you do not use string concatenation or interpolation when importing the SDK. This is because the SDK URL is a dynamic URL and the build system might not be able to resolve it during static analysis. +Make sure to replace the `` in the above string with the provided public API key. ::: ### Passing the Request ID to the Backend @@ -59,7 +59,7 @@ import EmailPassword from "supertokens-auth-react/recipe/emailpassword"; const ENVIRONMENT_ID = ""; // Your environment ID that you received from the SuperTokens team // Initialize the agent on page load using your public API key that you received from the SuperTokens team. -const supertokensRequestIdPromise = require("https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s?apiKey=") +const supertokensRequestIdPromise = require("https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/k9bwGCuvuA83Ad6s?apiKey=") .then((RequestId: any) => RequestId.load({ endpoint: [ 'https://deviceid.supertokens.io/PqWNQ35Ydhm6WDUK/CnsdzKsyFKU8Q3h2', From 770c798d536b14531f558eeee29fc1880c545ba2 Mon Sep 17 00:00:00 2001 From: Victor Bojica Date: Tue, 15 Oct 2024 18:28:50 +0300 Subject: [PATCH 4/4] added missing password check for sign up --- v2/attackprotectionsuite/backend-setup.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/attackprotectionsuite/backend-setup.mdx b/v2/attackprotectionsuite/backend-setup.mdx index a27e18af1..b42329dc2 100644 --- a/v2/attackprotectionsuite/backend-setup.mdx +++ b/v2/attackprotectionsuite/backend-setup.mdx @@ -510,7 +510,7 @@ SuperTokens.init({ const bruteForceConfig = getBruteForceConfig(email, ip, actionType); // we check the anomaly detection service before calling the original implementation of signUp - let securityCheckResponse = await handleSecurityChecks({ requestId, email, bruteForceConfig, actionType }); + let securityCheckResponse = await handleSecurityChecks({ requestId, email, password, bruteForceConfig, actionType }); if(securityCheckResponse !== undefined) { return securityCheckResponse; }