summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/chatty-teachers-sit.md5
-rw-r--r--.changeset/config.json2
-rw-r--r--.changeset/pre.json35
-rw-r--r--.changeset/quick-ads-exercise.md10
-rw-r--r--.changeset/small-ties-sort.md50
-rw-r--r--CONTRIBUTING.md10
-rwxr-xr-xpackages/astro/astro.js2
-rw-r--r--packages/astro/src/runtime/server/render/util.ts21
-rw-r--r--packages/astro/test/astro-attrs.test.js38
-rw-r--r--packages/astro/test/astro-component-code.test.js8
-rw-r--r--packages/astro/test/fixtures/astro-attrs/src/pages/index.astro35
-rw-r--r--packages/integrations/mdx/test/mdx-vite-env-vars.test.js4
-rw-r--r--packages/markdown/remark/src/shiki.ts30
13 files changed, 174 insertions, 76 deletions
diff --git a/.changeset/chatty-teachers-sit.md b/.changeset/chatty-teachers-sit.md
new file mode 100644
index 000000000..9e4fd89b4
--- /dev/null
+++ b/.changeset/chatty-teachers-sit.md
@@ -0,0 +1,5 @@
+---
+"astro": major
+---
+
+The lowest version of Node supported by Astro is now Node v18.17.1 and higher.
diff --git a/.changeset/config.json b/.changeset/config.json
index 030941db2..7920f52b8 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -4,7 +4,7 @@
"commit": false,
"linked": [],
"access": "public",
- "baseBranch": "main",
+ "baseBranch": "next",
"updateInternalDependencies": "patch",
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 000000000..f239e523d
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,35 @@
+{
+ "mode": "pre",
+ "tag": "alpha",
+ "initialVersions": {
+ "astro": "4.13.1",
+ "@astrojs/prism": "3.1.0",
+ "@astrojs/rss": "4.0.7",
+ "create-astro": "4.8.1",
+ "@astrojs/db": "0.12.0",
+ "@astrojs/alpinejs": "0.4.0",
+ "@astrojs/cloudflare": "0.0.0",
+ "@astrojs/lit": "4.3.0",
+ "@astrojs/markdoc": "0.11.3",
+ "@astrojs/mdx": "3.1.3",
+ "@astrojs/netlify": "0.0.0",
+ "@astrojs/node": "8.3.2",
+ "@astrojs/partytown": "2.1.1",
+ "@astrojs/preact": "3.5.1",
+ "@astrojs/react": "3.6.1",
+ "@astrojs/sitemap": "3.1.6",
+ "@astrojs/solid-js": "4.4.0",
+ "@astrojs/svelte": "5.7.0",
+ "@astrojs/tailwind": "5.1.0",
+ "@astrojs/vercel": "7.7.2",
+ "@astrojs/vue": "4.5.0",
+ "@astrojs/web-vitals": "1.0.0",
+ "@astrojs/internal-helpers": "0.4.1",
+ "@astrojs/markdown-remark": "5.2.0",
+ "@astrojs/studio": "0.1.1",
+ "@astrojs/telemetry": "3.1.0",
+ "@astrojs/underscore-redirects": "0.3.4",
+ "@astrojs/upgrade": "0.3.1"
+ },
+ "changesets": []
+}
diff --git a/.changeset/quick-ads-exercise.md b/.changeset/quick-ads-exercise.md
new file mode 100644
index 000000000..dd4285a4c
--- /dev/null
+++ b/.changeset/quick-ads-exercise.md
@@ -0,0 +1,10 @@
+---
+'@astrojs/markdown-remark': major
+---
+
+Renames the following CSS variables theme color token names to better align with the Shiki v1 defaults:
+
+- `--astro-code-color-text` => `--astro-code-foreground`
+- `--astro-code-color-background` => `--astro-code-background`
+
+You can perform a global find and replace in your project to migrate to the new token names.
diff --git a/.changeset/small-ties-sort.md b/.changeset/small-ties-sort.md
new file mode 100644
index 000000000..e3f3d67eb
--- /dev/null
+++ b/.changeset/small-ties-sort.md
@@ -0,0 +1,50 @@
+---
+'astro': major
+---
+
+Fixes attribute rendering for non-[boolean HTML attributes](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML) with boolean values to match proper attribute handling in browsers.
+
+Previously, non-boolean attributes may not have included their values when rendered to HTML. In Astro v5.0, the values are now explicitly rendered as `="true"` or `="false"`
+
+In the following `.astro` examples, only `allowfullscreen` is a boolean attribute:
+
+```astro
+<!-- src/pages/index.astro -->
+<!-- `allowfullscreen` is a boolean attribute -->
+<p allowfullscreen={true}></p>
+<p allowfullscreen={false}></p>
+
+<!-- `inherit` is *not* a boolean attribute -->
+<p inherit={true}></p>
+<p inherit={false}></p>
+
+<!-- `data-*` attributes are not boolean attributes -->
+<p data-light={true}></p>
+<p data-light={false}></p>
+```
+
+Astro v5.0 now preserves the full data attribute with its value when rendering the HTML of non-boolean attributes:
+
+```diff
+ <p allowfullscreen></p>
+ <p></p>
+
+ <p inherit="true"></p>
+- <p inherit></p>
++ <p inherit="false"></p>
+
+- <p data-light></p>
++ <p data-light="true"></p>
+- <p></p>
++ <p data-light="false"></p>
+```
+
+If you rely on attribute values, for example to locate elements or to conditionally render, update your code to match the new non-boolean attribute values:
+
+```diff
+- el.getAttribute('inherit') === ''
++ el.getAttribute('inherit') === 'false'
+
+- el.hasAttribute('data-light')
++ el.dataset.light === 'true'
+```
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2f2e63786..caaa9e726 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -369,20 +369,22 @@ Full documentation: https://github.com/changesets/changesets/blob/main/docs/prer
### Entering prerelease mode
-If you have gotten permission from the core contributors, you can enter into prerelease mode by following the following steps:
+If you have gotten permission from the core contributors, you can enter into prerelease mode with the following steps:
- Run: `pnpm exec changeset pre enter next` in the project root
+- Update `.changeset/config.json` with `"baseBranch": "next"` (for easier changesets creation)
- Create a new PR from the changes created by this command
-- Review, approve, and more the PR to enter prerelease mode.
+- Review, approve, and merge the PR to enter prerelease mode.
- If successful, The "[ci] release" PR (if one exists) will now say "[ci] release (next)".
### Exiting prerelease mode
-Exiting prerelease mode should happen once an experimental release is ready to go from `npm install astro@next` to `npm install astro`. Only a core contributor run these steps. These steps should be run before
+Exiting prerelease mode should happen once an experimental release is ready to go from `npm install astro@next` to `npm install astro`. Only a core contributor can run these steps:
- Run: `pnpm exec changeset pre exit` in the project root
+- Update `.changeset/config.json` with `"baseBranch": "main"`
- Create a new PR from the changes created by this command.
-- Review, approve, and more the PR to enter prerelease mode.
+- Review, approve, and merge the PR to enter prerelease mode.
- If successful, The "[ci] release (next)" PR (if one exists) will now say "[ci] release".
### Releasing `astro@latest` while in prerelease mode
diff --git a/packages/astro/astro.js b/packages/astro/astro.js
index 2000ca566..a02e60b76 100755
--- a/packages/astro/astro.js
+++ b/packages/astro/astro.js
@@ -12,7 +12,7 @@ const CI_INSTRUCTIONS = {
};
// Hardcode supported Node.js version so we don't have to read differently in CJS & ESM.
-const engines = '>=18.14.1';
+const engines = '>=18.17.1';
const skipSemverCheckIfAbove = 19;
/** `astro *` */
diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts
index 5dc821aa5..019bf9a40 100644
--- a/packages/astro/src/runtime/server/render/util.ts
+++ b/packages/astro/src/runtime/server/render/util.ts
@@ -8,9 +8,6 @@ export const voidElementNames =
/^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
const htmlBooleanAttributes =
/^(?:allowfullscreen|async|autofocus|autoplay|controls|default|defer|disabled|disablepictureinpicture|disableremoteplayback|formnovalidate|hidden|loop|nomodule|novalidate|open|playsinline|readonly|required|reversed|scoped|seamless|itemscope)$/i;
-const htmlEnumAttributes = /^(?:contenteditable|draggable|spellcheck|value)$/i;
-// Note: SVG is case-sensitive!
-const svgEnumAttributes = /^(?:autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i;
const AMPERSAND_REGEX = /&/g;
const DOUBLE_QUOTE_REGEX = /"/g;
@@ -67,13 +64,6 @@ export function addAttribute(value: any, key: string, shouldEscape = true) {
return '';
}
- if (value === false) {
- if (htmlEnumAttributes.test(key) || svgEnumAttributes.test(key)) {
- return markHTMLString(` ${key}="false"`);
- }
- return '';
- }
-
// compiler directives cannot be applied dynamically, log a warning and ignore.
if (STATIC_DIRECTIVES.has(key)) {
// eslint-disable-next-line no-console
@@ -115,11 +105,16 @@ Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the
}
// Boolean values only need the key
- if (value === true && (key.startsWith('data-') || htmlBooleanAttributes.test(key))) {
+ if (htmlBooleanAttributes.test(key)) {
+ return markHTMLString(value ? ` ${key}` : '');
+ }
+
+ // Other attributes with an empty string value can omit rendering the value
+ if (value === '') {
return markHTMLString(` ${key}`);
- } else {
- return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
}
+
+ return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
}
// Adds support for `<Component {...value} />
diff --git a/packages/astro/test/astro-attrs.test.js b/packages/astro/test/astro-attrs.test.js
index 2e020f8ea..a981a5b15 100644
--- a/packages/astro/test/astro-attrs.test.js
+++ b/packages/astro/test/astro-attrs.test.js
@@ -16,21 +16,41 @@ describe('Attributes', async () => {
const $ = cheerio.load(html);
const attrs = {
- 'false-str': { attribute: 'attr', value: 'false' },
- 'true-str': { attribute: 'attr', value: 'true' },
- false: { attribute: 'attr', value: undefined },
- true: { attribute: 'attr', value: 'true' },
- empty: { attribute: 'attr', value: '' },
+ 'boolean-attr-true': { attribute: 'allowfullscreen', value: '' },
+ 'boolean-attr-false': { attribute: 'allowfullscreen', value: undefined },
+ 'boolean-attr-string-truthy': { attribute: 'allowfullscreen', value: '' },
+ 'boolean-attr-string-falsy': { attribute: 'allowfullscreen', value: undefined },
+ 'boolean-attr-number-truthy': { attribute: 'allowfullscreen', value: '' },
+ 'boolean-attr-number-falsy': { attribute: 'allowfullscreen', value: undefined },
+ 'data-attr-true': { attribute: 'data-foobar', value: 'true' },
+ 'data-attr-false': { attribute: 'data-foobar', value: 'false' },
+ 'data-attr-string-truthy': { attribute: 'data-foobar', value: 'foo' },
+ 'data-attr-string-falsy': { attribute: 'data-foobar', value: '' },
+ 'data-attr-number-truthy': { attribute: 'data-foobar', value: '1' },
+ 'data-attr-number-falsy': { attribute: 'data-foobar', value: '0' },
+ 'normal-attr-true': { attribute: 'foobar', value: 'true' },
+ 'normal-attr-false': { attribute: 'foobar', value: 'false' },
+ 'normal-attr-string-truthy': { attribute: 'foobar', value: 'foo' },
+ 'normal-attr-string-falsy': { attribute: 'foobar', value: '' },
+ 'normal-attr-number-truthy': { attribute: 'foobar', value: '1' },
+ 'normal-attr-number-falsy': { attribute: 'foobar', value: '0' },
null: { attribute: 'attr', value: undefined },
undefined: { attribute: 'attr', value: undefined },
- 'html-boolean': { attribute: 'async', value: 'async' },
- 'html-boolean-true': { attribute: 'async', value: 'async' },
- 'html-boolean-false': { attribute: 'async', value: undefined },
'html-enum': { attribute: 'draggable', value: 'true' },
'html-enum-true': { attribute: 'draggable', value: 'true' },
'html-enum-false': { attribute: 'draggable', value: 'false' },
};
+ assert.ok(!/allowfullscreen=/.test(html), 'boolean attributes should not have values');
+ assert.ok(
+ !/id="data-attr-string-falsy"\s+data-foobar=/.test(html),
+ "data attributes should not have values if it's an empty string"
+ );
+ assert.ok(
+ !/id="normal-attr-string-falsy"\s+data-foobar=/.test(html),
+ "normal attributes should not have values if it's an empty string"
+ );
+
// cheerio will unescape the values, so checking that the url rendered unescaped to begin with has to be done manually
assert.equal(
html.includes('https://example.com/api/og?title=hello&description=somedescription'),
@@ -46,7 +66,7 @@ describe('Attributes', async () => {
for (const id of Object.keys(attrs)) {
const { attribute, value } = attrs[id];
const attr = $(`#${id}`).attr(attribute);
- assert.equal(attr, value);
+ assert.equal(attr, value, `Expected ${attribute} to be ${value} for #${id}`);
}
});
diff --git a/packages/astro/test/astro-component-code.test.js b/packages/astro/test/astro-component-code.test.js
index 6124e6054..bc8b5f172 100644
--- a/packages/astro/test/astro-component-code.test.js
+++ b/packages/astro/test/astro-component-code.test.js
@@ -88,13 +88,13 @@ describe('<Code>', () => {
.map((i, f) => (f.attribs ? f.attribs.style : 'no style found'))
.toArray(),
[
- 'background-color:var(--astro-code-color-background);color:var(--astro-code-color-text); overflow-x: auto;',
+ 'background-color:var(--astro-code-background);color:var(--astro-code-foreground); overflow-x: auto;',
'color:var(--astro-code-token-constant)',
'color:var(--astro-code-token-function)',
- 'color:var(--astro-code-color-text)',
+ 'color:var(--astro-code-foreground)',
'color:var(--astro-code-token-string-expression)',
- 'color:var(--astro-code-color-text)',
- ],
+ 'color:var(--astro-code-foreground)',
+ ]
);
});
diff --git a/packages/astro/test/fixtures/astro-attrs/src/pages/index.astro b/packages/astro/test/fixtures/astro-attrs/src/pages/index.astro
index 7ac96635f..8f2576650 100644
--- a/packages/astro/test/fixtures/astro-attrs/src/pages/index.astro
+++ b/packages/astro/test/fixtures/astro-attrs/src/pages/index.astro
@@ -1,19 +1,30 @@
-<span id="false-str" attr="false" />
-<span id="true-str" attr="true" />
-<span id="true" attr={true} />
-<span id="false" attr={false} />
-<span id="empty" attr="" />
+<span id="boolean-attr-true" allowfullscreen={true} />
+<span id="boolean-attr-false" allowfullscreen={false} />
+<span id="boolean-attr-string-truthy" allowfullscreen={'foo'} />
+<span id="boolean-attr-string-falsy" allowfullscreen={''} />
+<span id="boolean-attr-number-truthy" allowfullscreen={1} />
+<span id="boolean-attr-number-falsy" allowfullscreen={0} />
+
+<span id="data-attr-true" data-foobar={true} />
+<span id="data-attr-false" data-foobar={false} />
+<span id="data-attr-string-truthy" data-foobar={'foo'} />
+<span id="data-attr-string-falsy" data-foobar={''} />
+<span id="data-attr-number-truthy" data-foobar={1} />
+<span id="data-attr-number-falsy" data-foobar={0} />
+
+<span id="normal-attr-true" foobar={true} />
+<span id="normal-attr-false" foobar={false} />
+<span id="normal-attr-string-truthy" foobar={'foo'} />
+<span id="normal-attr-string-falsy" foobar={''} />
+<span id="normal-attr-number-truthy" foobar={1} />
+<span id="normal-attr-number-falsy" foobar={0} />
+
<span id="null" attr={null} />
<span id="undefined" attr={undefined} />
+
<span id="url" attr={"https://example.com/api/og?title=hello&description=somedescription"}/>
<span id="code" attr={"cmd: echo \"foo\" && echo \"bar\" > /tmp/hello.txt"} />
-<!--
- Per HTML spec, some attributes should be treated as booleans
- These should always render <span async /> or <span /> (without a string value)
--->
-<span id='html-boolean' async />
-<span id='html-boolean-true' async={true} />
-<span id='html-boolean-false' async={false} />
+
<!--
Other attributes should be treated as string enums
These should always render <span draggable="true" /> or <span draggable="false" />
diff --git a/packages/integrations/mdx/test/mdx-vite-env-vars.test.js b/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
index 80a9b1cec..213386ceb 100644
--- a/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
+++ b/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
@@ -57,8 +57,8 @@ describe('MDX - Vite env vars', () => {
const dataAttrDump = document.querySelector('[data-env-dump]');
assert.notEqual(dataAttrDump, null);
- assert.notEqual(dataAttrDump.getAttribute('data-env-prod'), null);
- assert.equal(dataAttrDump.getAttribute('data-env-dev'), null);
+ assert.equal(dataAttrDump.getAttribute('data-env-prod'), 'true');
+ assert.equal(dataAttrDump.getAttribute('data-env-dev'), 'false');
assert.equal(dataAttrDump.getAttribute('data-env-base-url'), '/');
assert.equal(dataAttrDump.getAttribute('data-env-mode'), 'production');
});
diff --git a/packages/markdown/remark/src/shiki.ts b/packages/markdown/remark/src/shiki.ts
index 0028ec927..011431e50 100644
--- a/packages/markdown/remark/src/shiki.ts
+++ b/packages/markdown/remark/src/shiki.ts
@@ -5,7 +5,6 @@ import {
getHighlighter,
isSpecialLang,
} from 'shiki';
-import { visit } from 'unist-util-visit';
import type { ShikiConfig } from './types.js';
export interface ShikiHighlighter {
@@ -23,16 +22,6 @@ export interface ShikiHighlighter {
): Promise<string>;
}
-// TODO: Remove this special replacement in Astro 5
-const ASTRO_COLOR_REPLACEMENTS: Record<string, string> = {
- '--astro-code-foreground': '--astro-code-color-text',
- '--astro-code-background': '--astro-code-color-background',
-};
-const COLOR_REPLACEMENT_REGEX = new RegExp(
- `${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')}`,
- 'g',
-);
-
let _cssVariablesTheme: ReturnType<typeof createCssVariablesTheme>;
const cssVariablesTheme = () =>
_cssVariablesTheme ??
@@ -145,21 +134,6 @@ export async function createShikiHighlighter({
return node.children[0] as typeof node;
}
},
- root(node) {
- if (Object.values(themes).length) {
- return;
- }
-
- const themeName = typeof theme === 'string' ? theme : theme.name;
- if (themeName === 'css-variables') {
- // Replace special color tokens to CSS variables
- visit(node as any, 'element', (child) => {
- if (child.properties?.style) {
- child.properties.style = replaceCssVariables(child.properties.style);
- }
- });
- }
- },
},
...transformers,
],
@@ -171,7 +145,3 @@ export async function createShikiHighlighter({
function normalizePropAsString(value: Properties[string]): string | null {
return Array.isArray(value) ? value.join(' ') : (value as string | null);
}
-
-function replaceCssVariables(str: string) {
- return str.replace(COLOR_REPLACEMENT_REGEX, (match) => ASTRO_COLOR_REPLACEMENTS[match] || match);
-}