Skip to content

Commit

Permalink
eslint plugin no-undeclared-env-vars should detect known framework va…
Browse files Browse the repository at this point in the history
…riables (#9110)
  • Loading branch information
dimitropoulos authored Sep 17, 2024
1 parent d0f4a0a commit b382e7e
Show file tree
Hide file tree
Showing 34 changed files with 1,453 additions and 1,234 deletions.
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"type": "shell",
"label": "prepare turbo",
"command": "cargo build -p turbo"
"command": "cargo build --package turbo"
}
]
}
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Dependencies

Building

- Building `turbo` CLI: `cargo build -p turbo`
- Building `turbo` CLI: `cargo build --package turbo`
- Using `turbo` to build `turbo` CLI: `./turbow.js`

### TLS Implementation
Expand Down
6 changes: 3 additions & 3 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"private": true,
"version": "0.0.0",
"scripts": {
"clean": "cargo clean -p turbo",
"build": "cargo build -p turbo",
"build:release": "cargo build -p turbo --profile release-turborepo"
"clean": "cargo clean --package turbo",
"build": "cargo build --package turbo",
"build:release": "cargo build --package turbo --profile release-turborepo"
}
}
146 changes: 24 additions & 122 deletions crates/turborepo-lib/src/framework.rs
Original file line number Diff line number Diff line change
@@ -1,146 +1,48 @@
use std::sync::OnceLock;

use serde::Deserialize;
use turborepo_repository::package_graph::PackageInfo;

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
enum Strategy {
All,
Some,
}

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Matcher {
strategy: Strategy,
dependencies: Vec<&'static str>,
dependencies: Vec<String>,
}

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Framework {
slug: &'static str,
env_wildcards: Vec<&'static str>,
slug: String,
env_wildcards: Vec<String>,
dependency_match: Matcher,
}

impl Framework {
pub fn slug(&self) -> &'static str {
self.slug
pub fn slug(&self) -> String {
self.slug.clone()
}

pub fn env_wildcards(&self) -> &[&'static str] {
pub fn env_wildcards(&self) -> &[String] {
&self.env_wildcards
}
}

static FRAMEWORKS: OnceLock<[Framework; 13]> = OnceLock::new();
static FRAMEWORKS: OnceLock<Vec<Framework>> = OnceLock::new();

fn get_frameworks() -> &'static [Framework] {
const FRAMEWORKS_JSON: &str =
include_str!("../../../packages/turbo-types/src/json/frameworks.json");

fn get_frameworks() -> &'static Vec<Framework> {
FRAMEWORKS.get_or_init(|| {
[
Framework {
slug: "blitzjs",
env_wildcards: vec!["NEXT_PUBLIC_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["blitz"],
},
},
Framework {
slug: "nextjs",
env_wildcards: vec!["NEXT_PUBLIC_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["next"],
},
},
Framework {
slug: "gatsby",
env_wildcards: vec!["GATSBY_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["gatsby"],
},
},
Framework {
slug: "astro",
env_wildcards: vec!["PUBLIC_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["astro"],
},
},
Framework {
slug: "solidstart",
env_wildcards: vec!["VITE_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["solid-js", "solid-start"],
},
},
Framework {
slug: "vue",
env_wildcards: vec!["VUE_APP_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["@vue/cli-service"],
},
},
Framework {
slug: "sveltekit",
env_wildcards: vec!["VITE_*", "PUBLIC_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["@sveltejs/kit"],
},
},
Framework {
slug: "create-react-app",
env_wildcards: vec!["REACT_APP_*"],
dependency_match: Matcher {
strategy: Strategy::Some,
dependencies: vec!["react-scripts", "react-dev-utils"],
},
},
Framework {
slug: "nitro",
env_wildcards: vec!["NITRO_*"],
dependency_match: Matcher {
strategy: Strategy::Some,
dependencies: vec!["nitropack", "nitropack-nightly"],
},
},
Framework {
slug: "nuxtjs",
env_wildcards: vec!["NUXT_*", "NITRO_*"],
dependency_match: Matcher {
strategy: Strategy::Some,
dependencies: vec!["nuxt", "nuxt-edge", "nuxt3", "nuxt3-edge"],
},
},
Framework {
slug: "redwoodjs",
env_wildcards: vec!["REDWOOD_ENV_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["@redwoodjs/core"],
},
},
Framework {
slug: "vite",
env_wildcards: vec!["VITE_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["vite"],
},
},
Framework {
slug: "sanity",
env_wildcards: vec!["SANITY_STUDIO_*"],
dependency_match: Matcher {
strategy: Strategy::All,
dependencies: vec!["@sanity/cli"],
},
},
]
serde_json::from_str(FRAMEWORKS_JSON).expect("Unable to parse embedded JSON")
})
}

Expand All @@ -159,16 +61,16 @@ impl Matcher {
Strategy::All => self
.dependencies
.iter()
.all(|dep| deps.map_or(false, |deps| deps.contains_key(*dep))),
.all(|dep| deps.map_or(false, |deps| deps.contains_key(dep))),
Strategy::Some => self
.dependencies
.iter()
.any(|dep| deps.map_or(false, |deps| deps.contains_key(*dep))),
.any(|dep| deps.map_or(false, |deps| deps.contains_key(dep))),
}
}
}

pub fn infer_framework(workspace: &PackageInfo, is_monorepo: bool) -> Option<&'static Framework> {
pub fn infer_framework(workspace: &PackageInfo, is_monorepo: bool) -> Option<&Framework> {
let frameworks = get_frameworks();

frameworks
Expand All @@ -183,7 +85,7 @@ mod tests {

use crate::framework::{get_frameworks, infer_framework, Framework};

fn get_framework_by_slug(slug: &str) -> &'static Framework {
fn get_framework_by_slug(slug: &str) -> &Framework {
get_frameworks()
.iter()
.find(|framework| framework.slug == slug)
Expand Down Expand Up @@ -291,7 +193,7 @@ mod tests {
)]
fn test_infer_framework(
workspace_info: PackageInfo,
expected: Option<&'static Framework>,
expected: Option<&Framework>,
is_monorepo: bool,
) {
let framework = infer_framework(&workspace_info, is_monorepo);
Expand Down
4 changes: 2 additions & 2 deletions crates/turborepo-telemetry/src/events/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ impl PackageTaskEventBuilder {
}

// event methods
pub fn track_framework(&self, framework: &str) -> &Self {
pub fn track_framework(&self, framework: String) -> &Self {
self.track(Event {
key: "framework".to_string(),
value: framework.to_string(),
value: framework,
is_sensitive: EventType::NonSensitive,
send_in_ci: false,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ description: Learn how to handle environments for your applications.
import { Callout } from '#/components/callout';
import { Tabs, Tab } from '#/components/tabs';
import { Accordion, Accordions } from '#/components/accordion';
import { frameworks } from '@turbo/types';

Environment variable inputs are a vital part of your applications that you'll need to account for in your Turborepo configuration.

Expand Down Expand Up @@ -56,21 +57,24 @@ Turborepo needs to be aware of your environment variables to account for changes

Turborepo automatically adds prefix wildcards to your [`env`](/repo/docs/reference/configuration#env) key for common frameworks. If you're using one of the frameworks below in a package, you don't need to specify environment variables with these prefixes:

| Framework | `env` wildcard |
| ---------------- | ------------------- |
| Astro | `PUBLIC_*` |
| Blitz | `NEXT_PUBLIC_*` |
| Create React App | `REACT_APP_*` |
| Gatsby | `GATSBY_*` |
| Next.js | `NEXT_PUBLIC_*` |
| Nitro | `NITRO_*` |
| Nuxt.js | `NUXT_*`, `NITRO_*` |
| RedwoodJS | `REDWOOD_ENV_*` |
| Sanity Studio | `SANITY_STUDIO_*` |
| Solid | `VITE_*` |
| SvelteKit | `VITE_*` |
| Vite | `VITE_*` |
| Vue | `VUE_APP_*` |
<table>
<thead>
<tr>
<th>Framework</th>
<th>
<code>env</code> wildcards
</th>
</tr>
</thead>
<tbody>
{frameworks.map(({ name, envWildcards }) => (
<tr key={name}>
<td>{name}</td>
<td>{envWildcards.map((w) => <code>{w}</code>).join(', ')}</td>
</tr>
))}
</tbody>
</table>

<Callout type="good-to-know">Framework inference is per-package.</Callout>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["plugin:turbo/recommended"],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
process.env.NEXT_PUBLIC_ZILTOID;
process.env.GATSBY_THE;
process.env.NITRO_OMNISCIENT;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "nextjs",
"dependencies": {
"next": "*",
"blitz": "*",
"react": "*",
"left-pad": "*",
"event-stream": "*",
"gatsby": "*",
"is-promise": "*",
"@faker-js/faker": "*",
"ua-parser-js": "*",
"nitropack": "*"
},
"devDependencies": {
"eslint": "8.57.0",
"eslint-plugin-turbo": "../../../../"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
process.env.NEXT_PUBLIC_ZILTOID;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "nextjs",
"dependencies": {
"next": "*"
},
"devDependencies": {
"eslint": "8.57.0",
"eslint-plugin-turbo": "../../../../"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
process.env.VITE_THING;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "vite",
"dependencies": {
"vite": "*"
},
"devDependencies": {
"eslint": "8.57.0",
"eslint-plugin-turbo": "../../../../"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "framework-inference",
"devDependencies": {
"eslint": "8.57.0",
"eslint-plugin-turbo": "../../"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
Loading

0 comments on commit b382e7e

Please sign in to comment.