diff --git a/sx/sxc/pages/__init__.py b/sx/sxc/pages/__init__.py index 7a19bc7..37bd447 100644 --- a/sx/sxc/pages/__init__.py +++ b/sx/sxc/pages/__init__.py @@ -2442,19 +2442,33 @@ def _essay_sx_native() -> str: def _essay_sx_manifesto() -> str: p = '(p :class "text-stone-600"' em = '(span :class "italic"' + L = "text-violet-600 hover:underline" + + def x(url: str, text: str) -> str: + """External link.""" + return f'(a :href "{url}" :class "{L}" :target "_blank" "{text}")' + + def i(path: str, text: str) -> str: + """Internal SX app link.""" + return (f'(a :href "{path}" :sx-get "{path}" :sx-target "#main-panel"' + f' :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true"' + f' :class "{L}" "{text}")') + return ( '(~doc-page :title "The SX Manifesto"' ' (p :class "text-stone-500 text-sm italic mb-8"' - ' "After " (a :href "https://www.marxists.org/archive/marx/works/1848/communist-manifesto/"' - ' :class "text-violet-600 hover:underline" "Marx & Engels") ", loosely")' + f' "After " {x("https://www.marxists.org/archive/marx/works/1848/communist-manifesto/", "Marx & Engels")} ", loosely")' # --- I. A spectre is haunting the web --- ' (~doc-section :title "I. A spectre is haunting the web" :id "spectre"' f' {p}' - ' "A spectre is haunting the web — the spectre of s-expressions. ' - 'All the powers of the old web have entered into a holy alliance to exorcise this spectre: ' - 'Google and Meta, webpack and Vercel, Stack Overflow moderators and DevRel influencers.")' + ' "A spectre is haunting the web — the spectre of ' + f's-expressions. ' + f'All the powers of the old web have entered into a holy alliance to exorcise this spectre: "' + f' {x("https://google.com", "Google")} " and " {x("https://about.meta.com", "Meta")} ", "' + f' {x("https://webpack.js.org", "webpack")} " and " {x("https://vercel.com", "Vercel")} ", "' + f' {x("https://stackoverflow.com", "Stack Overflow")} " moderators and DevRel influencers.")' f' {p}' ' "Where is the rendering paradigm that has not been decried as ' 'a step backward by its opponents? Where is the framework that has not ' @@ -2480,15 +2494,23 @@ def _essay_sx_manifesto() -> str: ' "Markup and logic, template and script, structure and style — ' 'in a word, oppressor and oppressed — stood in constant opposition to one another, ' 'carried on an uninterrupted, now hidden, now open fight, ' - 'a fight that each time ended in a laborious reconfiguration of webpack.")' + f'a fight that each time ended in a laborious reconfiguration of " {x("https://webpack.js.org", "webpack")} ".")' f' {p}' ' "In the earlier epochs of web development we find almost everywhere ' 'a complicated arrangement of separate languages into various orders, ' 'a manifold gradation of technical rank: ' - 'HTML, CSS, JavaScript, XML, XSLT, JSON, YAML, TOML, JSX, TSX, ' - 'Sass, Less, PostCSS, Tailwind, and above them all, the build step.")' + f'HTML, CSS, JavaScript, " {x("https://www.w3.org/XML/", "XML")} ", "' + f' {x("https://www.w3.org/TR/xslt/", "XSLT")} ", "' + f' {x("https://www.json.org", "JSON")} ", "' + f' {x("https://yaml.org", "YAML")} ", "' + f' {x("https://toml.io", "TOML")} ", "' + f' {x("https://react.dev/learn/writing-markup-with-jsx", "JSX")} ", TSX, "' + f' {x("https://sass-lang.com", "Sass")} ", "' + f' {x("https://lesscss.org", "Less")} ", "' + f' {x("https://postcss.org", "PostCSS")} ", "' + f' {x("https://tailwindcss.com", "Tailwind")} ", and above them all, the build step.")' f' {p}' - ' "The modern web, sprouted from the ruins of CGI-bin, ' + f' "The modern web, sprouted from the ruins of " {x("https://en.wikipedia.org/wiki/Common_Gateway_Interface", "CGI-bin")} ", ' 'has not done away with language antagonisms. It has but established new languages, ' 'new conditions of oppression, new forms of struggle in place of the old ones.")' f' {p}' @@ -2513,7 +2535,7 @@ def _essay_sx_manifesto() -> str: 'Like every revolutionary who becomes a tyrant, ' 'it kept the worst habits of the regime it overthrew: ' 'weak typing, prototype chains, and the "' - f' {em} \"this\")' + f' {em} \\"this\\")' ' " keyword.")' f' {p}' ' "CSS, the third estate, controls all visual presentation ' @@ -2522,7 +2544,7 @@ def _essay_sx_manifesto() -> str: 'It has no variables. Then it had variables. ' 'It has no nesting. Then it had nesting. ' 'It is not a programming language. Then it was Turing-complete. ' - 'CSS is the Vicar of Bray of web technologies — ' + f'CSS is the " {x("https://en.wikipedia.org/wiki/The_Vicar_of_Bray", "Vicar of Bray")} " of web technologies — ' 'loyal to whichever paradigm currently holds power.")' f' {p}' ' "These three languages rule by enforced separation. ' @@ -2537,8 +2559,18 @@ def _essay_sx_manifesto() -> str: ' (~doc-section :title "IV. The petty-bourgeois frameworks" :id "frameworks"' f' {p}' ' "Between the ruling languages and the oppressed developer, ' - 'a vast class of intermediaries has arisen: the frameworks. ' - 'React, Vue, Angular, Svelte, Solid, Qwik, Astro, Next, Nuxt, Remix, Gatsby, ' + f'a vast class of intermediaries has arisen: the frameworks. "' + f' {x("https://react.dev", "React")} ", "' + f' {x("https://vuejs.org", "Vue")} ", "' + f' {x("https://angular.dev", "Angular")} ", "' + f' {x("https://svelte.dev", "Svelte")} ", "' + f' {x("https://www.solidjs.com", "Solid")} ", "' + f' {x("https://qwik.dev", "Qwik")} ", "' + f' {x("https://astro.build", "Astro")} ", "' + f' {x("https://nextjs.org", "Next")} ", "' + f' {x("https://nuxt.com", "Nuxt")} ", "' + f' {x("https://remix.run", "Remix")} ", "' + f' {x("https://www.gatsbyjs.com", "Gatsby")} ", ' 'and ten thousand others whose names will not survive the decade.")' f' {p}' ' "The frameworks are the petty bourgeoisie of web development. ' @@ -2547,27 +2579,32 @@ def _essay_sx_manifesto() -> str: 'extracting rent in the form of configuration files, ' 'build pipelines, and breaking changes.")' f' {p}' - ' "Each framework promises liberation. Each framework delivers a new dependency tree. ' - 'React freed us from manual DOM manipulation and gave us a virtual DOM, ' + f' "Each framework promises liberation. Each framework delivers a new dependency tree. "' + f' {x("https://react.dev", "React")} " freed us from manual DOM manipulation and gave us a "' + f' {x("https://legacy.reactjs.org/docs/faq-internals.html", "virtual DOM")} ", ' 'a reconciler, hooks with seventeen rules, ' - 'and a conference circuit. ' - 'Vue freed us from React\'s complexity and gave us the Options API, ' - 'then the Composition API, then told us the Options API was fine actually. ' - 'Angular freed us from choice and gave us a CLI that generates eleven files ' - 'to display \\\"Hello World.\\\" ' - 'Svelte freed us from the virtual DOM and gave us a compiler. ' - 'SolidJS freed us from React\'s re-rendering and gave us signals, ' + f'and a conference circuit. "' + f' {x("https://vuejs.org", "Vue")} " freed us from React\'s complexity and gave us the Options API, ' + f'then the Composition API, then told us the Options API was fine actually. "' + f' {x("https://angular.dev", "Angular")} " freed us from choice and gave us a CLI that generates eleven files ' + f'to display \\\"Hello World.\\\" "' + f' {x("https://svelte.dev", "Svelte")} " freed us from the virtual DOM and gave us a compiler. "' + f' {x("https://www.solidjs.com", "SolidJS")} " freed us from React\'s re-rendering and gave us signals, ' 'which React then adopted, completing the circle.")' f' {p}' ' "The frameworks reproduce the very conditions they claim to abolish. ' 'They bridge the gap between HTML, JavaScript, and CSS ' - 'by adding a fourth language — JSX, SFCs, templates — ' + f'by adding a fourth language — " {x("https://react.dev/learn/writing-markup-with-jsx", "JSX")} ", "' + f' {x("https://vuejs.org/guide/scaling-up/sfc.html", "SFCs")} ", templates — ' 'which must itself be compiled back into the original three. ' 'The revolution merely adds a build step.")' f' {p}' ' "And beside the frameworks stand the libraries — ' - 'the lumpenproletariat of the ecosystem. ' - 'Lodash, Moment, Axios, left-pad. ' + f'the lumpenproletariat of the ecosystem. "' + f' {x("https://lodash.com", "Lodash")} ", "' + f' {x("https://momentjs.com", "Moment")} ", "' + f' {x("https://axios-http.com", "Axios")} ", "' + f' {x("https://www.npmjs.com/package/left-pad", "left-pad")} ". ' 'They attach themselves to whichever framework currently holds power, ' 'contributing nothing original, merely wrapping what already exists, ' 'adding weight to the node_modules directory until it exceeds the mass of the sun."))' @@ -2576,19 +2613,24 @@ def _essay_sx_manifesto() -> str: ' (~doc-section :title "V. The build step as the state apparatus" :id "build-step"' f' {p}' - ' "The build step is the state apparatus of the framework bourgeoisie. ' - 'It enforces the class structure. It compiles JSX into createElement calls. ' - 'It transforms TypeScript into JavaScript. It processes Sass into CSS. ' + f' "The build step is the state apparatus of the framework bourgeoisie. ' + f'It enforces the class structure. It compiles " {x("https://react.dev/learn/writing-markup-with-jsx", "JSX")} " into createElement calls. ' + f'It transforms " {x("https://www.typescriptlang.org", "TypeScript")} " into JavaScript. ' + f'It processes " {x("https://sass-lang.com", "Sass")} " into CSS. ' 'It tree-shakes. It code-splits. It hot-module-replaces. ' 'It does everything except let you write code and run it.")' f' {p}' - ' "webpack begat Rollup. Rollup begat Parcel. Parcel begat esbuild. ' - 'esbuild begat Vite. Vite begat Turbopack. ' + f' " " {x("https://webpack.js.org", "webpack")} " begat " {x("https://rollupjs.org", "Rollup")} ". ' + f'Rollup begat " {x("https://parceljs.org", "Parcel")} ". ' + f'Parcel begat " {x("https://esbuild.github.io", "esbuild")} ". ' + f'esbuild begat " {x("https://vite.dev", "Vite")} ". ' + f'Vite begat " {x("https://turbo.build/pack", "Turbopack")} ". ' 'Each new bundler promises to be the last bundler. ' 'Each new bundler is faster than the last at doing something ' 'that should not need to be done at all.")' f' {p}' - ' "The build step exists because the ruling languages cannot express components. ' + ' "The build step exists because the ruling languages cannot express ' + f'" {i("/docs/components", "components")} ". ' 'HTML has no composition model. CSS has no scoping. JavaScript has no template syntax. ' 'The build step papers over these failures with transpilation, ' 'and calls it developer experience."))' @@ -2597,7 +2639,7 @@ def _essay_sx_manifesto() -> str: ' (~doc-section :title "VI. The s-expression revolution" :id "revolution"' f' {p}' - ' "The s-expression abolishes the language distinction itself. ' + f' "The " {x("https://en.wikipedia.org/wiki/S-expression", "s-expression")} " abolishes the language distinction itself. ' 'There is no HTML. There is no separate JavaScript. ' 'There is no CSS-as-a-separate-language. ' 'There is only the expression.")' @@ -2605,23 +2647,23 @@ def _essay_sx_manifesto() -> str: ' "Code is data. Data is DOM. DOM is code. ' 'The dialectical unity that HTML, JavaScript, and CSS ' 'could never achieve — because they are three languages pretending to be one system — ' - 'is the natural state of the s-expression, ' - 'which has been one language since 1958.")' + f'is the natural state of the s-expression, ' + f'which has been one language " {x("https://en.wikipedia.org/wiki/Lisp_(programming_language)", "since 1958")} ".")' f' {p}' - ' "The component is not a class, not a function, not a template. ' + f' "The " {i("/docs/components", "component")} " is not a class, not a function, not a template. ' 'The component is a list whose first element is a symbol. ' 'Composition is nesting. Abstraction is binding. ' - 'There is no JSX because there is no gap between the expression language ' + f'There is no JSX because there is no gap between the expression language ' 'and the thing being expressed.")' f' {p}' ' "The build step is abolished because there is nothing to compile. ' 'S-expressions are already in their final form. ' - 'The parser is thirty lines. The evaluator is fifty primitives. ' + f'The parser is thirty lines. The " {i("/docs/evaluator", "evaluator")} " is fifty " {i("/docs/primitives", "primitives")} ". ' 'The same source runs on server and client without transformation.")' f' {p}' ' "The framework is abolished because the language is the framework. ' - 'defcomp replaces the component model. defmacro replaces the plugin system. ' - 'The evaluator replaces the runtime. ' + f'" {i("/docs/components", "defcomp")} " replaces the component model. defmacro replaces the plugin system. ' + f'The " {i("/docs/evaluator", "evaluator")} " replaces the runtime. ' 'What remains is not a framework but a language — ' 'and languages do not have breaking changes between minor versions."))' @@ -2630,29 +2672,29 @@ def _essay_sx_manifesto() -> str: ' (~doc-section :title "VII. Objections from the bourgeoisie" :id "objections"' f' {p}' ' "\\\"You would destroy the separation of concerns!\\\" they cry. ' - 'The separation of concerns was destroyed long ago. ' - 'React components contain markup, logic, and inline styles. ' - 'Vue single-file components put template, script, and style in one file. ' - 'Tailwind puts styling in the markup. ' + f'The separation of concerns was destroyed long ago. "' + f' {x("https://react.dev", "React")} " components contain markup, logic, and inline styles. "' + f' {x("https://vuejs.org/guide/scaling-up/sfc.html", "Vue single-file components")} " put template, script, and style in one file. "' + f' {x("https://tailwindcss.com", "Tailwind")} " puts styling in the markup. ' 'The separation of concerns has been dead for years; ' 'the ruling classes merely maintain the pretence at conferences.")' f' {p}' - ' "\\\"Nobody uses s-expressions!\\\" they cry. ' - 'Emacs has been running on s-expressions since 1976. ' - 'Clojure runs Fortune 500 backends on s-expressions. ' - 'Every Lisp programmer who ever lived has known what the web refuses to admit: ' + ' "\\\"Nobody uses s-expressions!\\\" they cry. "' + f' {x("https://www.gnu.org/software/emacs/", "Emacs")} " has been running on s-expressions since 1976. "' + f' {x("https://clojure.org", "Clojure")} " runs Fortune 500 backends on s-expressions. ' + f'Every " {x("https://en.wikipedia.org/wiki/Lisp_(programming_language)", "Lisp")} " programmer who ever lived has known what the web refuses to admit: ' 'that the parenthesis is not a bug but the minimal syntax for structured data.")' f' {p}' ' "\\\"Where is the ecosystem?\\\" they cry. ' - 'The ecosystem is the problem. Two million npm packages, ' + f'The ecosystem is the problem. Two million " {x("https://www.npmjs.com", "npm")} " packages, ' 'of which fourteen are useful and the rest are competing implementations ' - 'of is-odd. The s-expression needs no ecosystem because ' + f'of " {x("https://www.npmjs.com/package/is-odd", "is-odd")} ". The s-expression needs no ecosystem because ' 'the language itself provides what packages exist to paper over: ' 'composition, abstraction, and code-as-data.")' f' {p}' - ' "\\\"But TypeScript!\\\" they cry. TypeScript is a type system ' - 'bolted onto a language that was designed in ten days ' - 'by a man who wanted to write Scheme. ' + f' "\\\"But " {x("https://www.typescriptlang.org", "TypeScript")} "!\\\" they cry. TypeScript is a type system ' + f'bolted onto a language that was designed in ten days ' + f'by " {x("https://en.wikipedia.org/wiki/Brendan_Eich", "a man")} " who wanted to write " {x("https://en.wikipedia.org/wiki/Scheme_(programming_language)", "Scheme")} ". ' 'We have simply completed his original vision.")' f' {p}' ' "\\\"You have no jobs!\\\" they cry. Correct. ' @@ -2670,22 +2712,22 @@ def _essay_sx_manifesto() -> str: 'it is the collaborator class, providing aesthetic legitimacy ' 'to whichever regime currently holds power.")' f' {p}' - ' "CSS-in-JS was the first attempt at annexation: ' + f' " " {x("https://en.wikipedia.org/wiki/CSS-in-JS", "CSS-in-JS")} " was the first attempt at annexation: ' 'JavaScript consuming CSS entirely, reducing it to template literals ' 'and runtime overhead. ' 'This provocation produced the counter-revolution of utility classes — ' - 'Tailwind — which reasserted CSS\'s independence ' + f'" {x("https://tailwindcss.com", "Tailwind")} " — which reasserted CSS\'s independence ' 'by making the developer write CSS in HTML attributes ' 'while insisting this was not inline styles.")' f' {p}' ' "The s-expression resolves the CSS question by eliminating it. ' - 'Styles are expressions. "' + f'Styles are " {i("/docs/css", "expressions")} ". "' ' (code :class "text-violet-700" "(css :flex :gap-4 :p-2)")' ' " is not a class name, not an inline style, not a CSS-in-JS template literal. ' 'It is a function call that returns a value. ' 'The value produces a generated class. ' - 'The class is delivered on demand. ' - 'No build step. No runtime overhead. No Tailwind config.")' + f'The class is " {i("/essays/on-demand-css", "delivered on demand")} ". ' + f'No build step. No runtime overhead. No " {x("https://tailwindcss.com", "Tailwind")} " config.")' f' {p}' ' "Code is data is DOM is " ' f' {em} "style") "."))' @@ -2704,12 +2746,12 @@ def _essay_sx_manifesto() -> str: ' (ol :class "space-y-2 text-stone-600 mt-2 list-decimal list-inside"' ' (li "Abolition of the build step and all its instruments of compilation")' ' (li "Abolition of the framework as a class distinct from the language")' - ' (li "Centralisation of rendering in the hands of a single evaluator, ' + f' (li "Centralisation of rendering in the hands of a single " {i("/docs/evaluator", "evaluator")} ", ' 'running identically on server and client")' ' (li "Abolition of the language distinction between structure, style, and behaviour")' ' (li "Equal obligation of all expressions to be data as well as code")' - ' (li "Gradual abolition of the distinction between server and client ' - 'by means of a uniform wire protocol")' + f' (li "Gradual abolition of the distinction between server and client ' + f'by means of a uniform " {i("/protocols/wire-format", "wire protocol")} ")' ' (li "Free evaluation for all expressions in public and private environments")' ' (li "Abolition of the node_modules directory " ' ' (span :class "text-stone-400 italic" "(this alone justifies the revolution)")))'