Fork me on GitHub
#shadow-cljs
<
2018-11-21
>
beders05:11:45

shadow-cljs is full of treasures. Just discovered the proxy option. Works really well (except for POSTs).

beders05:11:09

Is there a way I can determine if my code is running in dev mode?

beders05:11:23

for example, when running in dev, I'm talking to an API that runs on a different port number

beders05:11:33

in production however, I'm not

beders05:11:35

something like (if (find-ns 'shadow)...) maybe?

lilactown05:11:07

check js/goog.DEBUG

thheller08:11:50

@beders what do you mean except for POSTs?

beders15:11:37

I didn't look deeper into it, but POST requests didn't make it through the proxy. In that case I was talking to a sente endpoint which would like to upgrade the connection to a websocket.

thheller16:11:43

which version are you on? all requests should work just fine

thheller16:11:20

older versions didn't handle this correctly. 2.7.x should be fine though

👍 4
beders00:11:28

thank you! As usual, I missed the slack reply 😞

thheller08:11:31

use https://shadow-cljs.github.io/docs/UsersGuide.html#closure-defines for build specific options js/goog.DEBUG is one of them

cmal09:11:15

hi, @thheller. Today I have a clean machine build and have update the version of re-frame . shadow-cljs gives me so many warnings, but I think things such as Use of undeclared Var re-frame.events/java re-frame.events/java are not exist in the source code. The error messages are:

thheller09:11:10

@cmal yeah this is due to a new check in 2.7.x. already reported here https://github.com/Day8/re-frame-10x/issues/219

thheller09:11:04

oh wait .. they are in re-frame itself as well? guess I should report it there too

cmal09:11:58

maybe, I have the following deps:

cmal09:11:09

[re-frame "0.10.6"]
  [day8.re-frame/undo "0.3.2"]
  [day8.re-frame/http-fx "0.1.7"]
  [com.smxemail/re-frame-cookie-fx "0.0.2" ]

cmal10:11:08

And the warnings certainly come from re-frame.

thheller10:11:05

you can downgrade shadow-cljs to 2.6.24 for now

cmal10:11:48

ok, Thanks.

thheller10:11:28

no that seems to be fixed?

thheller10:11:40

I don't get those warnings?

thheller10:11:07

@cmal are you sure the re-frame is actually using that version?

thheller10:11:43

so it appears that you have some older version?

thheller10:11:20

run shadow-cljs clj-repl and ( "re_frame/trace.cljc") to check which file is actually getting used

cmal10:11:58

it is 0.10.2. I removed .shadow-cljs and compiled again. Another question, @thheller. https://github.com/funcool/cats/blob/master/project.clj project like this uses (defproject funcool/cats ...), how should I require this? I use [cats.core :as m] but shadow-cljs tells me The required namespace "cats.core" is not available, it was required by "doumi/helper.cljs"., while [funcool/cats.core :as m] just syntax error.

thheller10:11:01

@cmal the defproject name is only relevant for the dependency itself. it has nothing to do with the namespaces you use in CLJS

thheller10:11:52

ie. you depend on [funcool/cats "2.3.1"] but (:require [cats.core ...])

thheller10:11:38

removing .shadow-cljs should not affect anything in your dependencies?

cmal10:11:15

I just add [funcool/cats "2.3.1"] this to my deps.

cmal10:11:34

but cannot figure out a way to use it in the source code.

cmal10:11:06

[cats.core :as m] will get the error The required namespace "cats.core" is not available, it was required by "doumi/helper.cljs"

thheller10:11:09

you do not use lein/`deps.edn` right?

thheller10:11:23

you only have your :dependencies in shadow-cljs.edn?

cmal10:11:55

{:dependencies
 [[cljs-ajax "0.7.2"]
  [reagent "0.8.0-alpha2"]
  [reagent-utils "0.3.1"]
  [re-frame "0.10.6"]
  [day8.re-frame/undo "0.3.2"]
  [day8.re-frame/http-fx "0.1.7"]
  [secretary "1.2.3"]
  [garden "1.3.3"]
  [funcool/cats "2.3.1"] ;; <- just added this line
  [com.andrewmcveigh/cljs-time "0.5.2"]
  [com.smxemail/re-frame-cookie-fx "0.0.2" ]
  [com.taoensso/timbre "4.10.0"]
  [hiccups "0.3.0"]
  [thheller/shadow-cljsjs "0.0.16"]
  [org.clojure/core.async "0.4.474"]]
shadow-cljs.edn

thheller10:11:42

no :lein or :deps keys? please confirm that

cmal10:11:43

So I should lein install this?

cmal10:11:02

no :lein no :deps

thheller10:11:33

ok. did you restart the server process properly?

thheller10:11:43

after adding that line?

cmal10:11:08

just shadow-cljs watch doumi. not start another server.

thheller10:11:28

what did the watch say when you started

thheller10:11:32

connected to server?

thheller10:11:42

you may have another server process still running somewhere

cmal10:11:51

shadow-cljs watch doumi
shadow-cljs - config: /Users/yuzhao/gits/mobile/cljs/shadow-cljs.edn  cli version: 2.7.4  node: v10.13.0
shadow-cljs - starting ...
11月 21, 2018 6:28:38 下午 org.xnio.Xnio <clinit>
INFO: XNIO version 3.3.8.Final
11月 21, 2018 6:28:38 下午 org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.3.8.Final
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.xnio.nio.NioXnio$2 (file:/Users/yuzhao/.m2/repository/org/jboss/xnio/xnio-nio/3.3.8.Final/xnio-nio-3.3.8.Final.jar) to constructor sun.nio.ch.KQueueSelectorProvider()
WARNING: Please consider reporting this to the maintainers of org.xnio.nio.NioXnio$2
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
shadow-cljs - server version: 2.7.4
shadow-cljs - server running at 
shadow-cljs - socket REPL running on port 53798
shadow-cljs - nREPL server started on port 53800
shadow-cljs - watching build :doumi
[:doumi] Configuring build.
[:doumi] Compiling ...
^[[:doumi] Build failure:
The required namespace "cats.core" is not available, it was required by "doumi/helper.cljs".

thheller10:11:27

hmm that looks like it should

thheller10:11:35

something odd is going on in your system

thheller10:11:03

this was AFTER adding the line and saving the file?

cmal10:11:35

shadow-cljs watch doumi                                                                                                                                                                                                                                                                                                                                 [2h0m] ✹ ✭
shadow-cljs - config: /Users/yuzhao/gits/mobile/cljs/shadow-cljs.edn  cli version: 2.7.4  node: v10.13.0
shadow-cljs - starting ...
11月 21, 2018 6:30:21 下午 org.xnio.Xnio <clinit>
INFO: XNIO version 3.3.8.Final
11月 21, 2018 6:30:21 下午 org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.3.8.Final
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.xnio.nio.NioXnio$2 (file:/Users/yuzhao/.m2/repository/org/jboss/xnio/xnio-nio/3.3.8.Final/xnio-nio-3.3.8.Final.jar) to constructor sun.nio.ch.KQueueSelectorProvider()
WARNING: Please consider reporting this to the maintainers of org.xnio.nio.NioXnio$2
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
shadow-cljs - server version: 2.7.4
shadow-cljs - server running at 
shadow-cljs - socket REPL running on port 53912
shadow-cljs - nREPL server started on port 53914
shadow-cljs - watching build :doumi
[:doumi] Configuring build.
[:doumi] Compiling ...
[:doumi] Build failure:
The required namespace "cats.core" is not available, it was required by "doumi/helper.cljs".

cmal10:11:44

run several times.

cmal10:11:48

same error

thheller10:11:10

same deal shadow-cljs clj-repl and ( "cats/core.cljc")

cmal10:11:44

[1:0]~shadow.user=> ( "cats/core.cljc")
nil

thheller10:11:49

so then it is not on the classpath?

thheller10:11:02

are you sure you edited the correct shadow-cljs.edn?

thheller10:11:17

/Users/yuzhao/gits/mobile/cljs/shadow-cljs.edn

cmal10:11:18

Sorry for that.

cmal10:11:45

I am editing the file in the old machine.

mrg12:11:44

I'm trying to use different configurations based on whether i'm in the dev environment, or in release mode

mrg12:11:58

In leiningen I would have the same configuration namespace in two different environment paths, and add them to :source-paths conditionally

mrg12:11:12

I tried this with shadow-cljs but I' getting the error ":source-paths is only valid globally and cannot be configured in build :app."

mrg12:11:42

I haven't found a way of getting different things to happen in dev vs release. Help, please?

thheller12:11:10

I consider this :source-paths hack an anti-pattern and its not supported by shadow-cljs itself. you can still use it when using deps.edn or lein if you really want to

mrg12:11:25

I saw your post about it 🙂

mrg12:11:04

But I hadn't found a way to get this to work

thheller12:11:55

what exactly?

mrg12:11:07

Getting different things to happen at release vs dev time

thheller12:11:19

define "things"

mrg12:11:34

In my case, loading different configuration

thheller12:11:34

there are plenty of options via the links I posted above

mrg12:11:45

Yeah, I think closure-defines will work

mrg12:11:52

Thank you for that!

thheller12:11:10

how do you define "loading different configuration"?

mrg12:11:49

I don't understand the question. I have one s3 bucket that I want to use while developing, and another one that I want to use in the released app

thheller12:11:27

ok. I define that as "runtime configuration" and not "build configuration"

mrg12:11:31

I want to choose this bucket based on the run environment, so I don't forget to un/comment things

mrg12:11:44

Yep, that's it

thheller12:11:52

I should really document this somewhere ...

thheller12:11:12

so I do recommend that you have an init function in your app

thheller12:11:27

your.app.init() or something

thheller12:11:38

either called from the HTML via <script> or any other means really

thheller12:11:51

that init function can either load the config itself via XHR

thheller12:11:00

or via some other variable set in the HTML

thheller12:11:21

<script>var app$config = {"s3url":"...."};</script>

thheller12:11:43

and the init function then reads that and initializes for re-frame state or whatever you are using

thheller12:11:16

that way your HTML can control the stuff which makes it easy to change and doesn't require recompiling your CLJS when those things change

mrg12:11:37

But I still need to figure out either which runtime environment I am in, or use different HTML files depending on that, right?

mrg12:11:53

So I'm still back to closure-defines

thheller12:11:07

do you have a server part? or just a static HTML file?

mrg12:11:14

just static html

thheller12:11:45

well yeah you could just have a index.html + index-dev.html

mrg12:11:03

why would you use that approach over closure-defines?

mrg12:11:21

it seems easier to me to just set an environment key and act based on that?

thheller12:11:25

well I typically assume some kind of webserver that generates the HTML

thheller12:11:58

since the config I assign is typically dependent on some server-side stuff where recompiling the CLJS would be overkill

mrg12:11:01

In my case I'm acting on data from a 3rd party server that I don't have control over

mrg12:11:05

Makes sense

thheller12:11:13

but closure-defines is totally fine

mrg12:11:38

I'll give that a shot

mrg12:11:40

Thanks a lot

mrg12:11:51

And since I have you here, thank you so much for shadow-cljs

👍 4
mrg13:11:04

It's taken a lot of pain out of CLJS development for me

mrg13:11:32

to the point where I used to hem and haw a lot about starting a new project for fear of the fiddly configuration

thheller13:11:39

very welcome 🙂

mrg13:11:24

closure-defines worked well! Thanks and have a great day!

eoliphant17:11:36

Hi, i’m running into an issue where dev/watch builds work just fine. But a release build is failing with

Uncaught ReferenceError: __webpack_require__ is not defined
    at eval (index.js:1)
    at Object../components/index.js (app.js:330)
    at c (app.js:234)
    at ./components/Button/Button.jsx (app.js:236)
    at Object.shadow$provide.module$node_modules$$grantsolutions$grant_solutions_ui$dist$index (app.js:236)
    at oI (app.js:1362)
    at app.js:2070
    at app.js:2164
Fortunately, this is a react lib that’s being developed by another group, so if I can figure out what the problem is I can have them tweak the build to fix this. Any offhand ideas re: what might cause this. In poking around in the code, so far haven’t seen some of the other stuff that’s caused issues

lilactown17:11:24

how is the lib packaged?

lilactown17:11:32

is it ES5? is it a single bundle?

eoliphant17:11:39

looks like ES5 and JSX. here’s a bit of it unpacked

...
;; example of how we're pulling it in 
["@grantsolutions/grant-solutions-ui" :as gsui]

(def Button (uc/component-factory gsui/Button))

lilactown17:11:06

if it’s actually trying to run JSX code then it’s not going to work

lilactown17:11:19

you need to look in the dist folder

eoliphant17:11:36

ah hell i knew that lol, one sec

eoliphant17:11:15

ok yeah, looks like a bundle

eoliphant17:11:30

__webpack_require__ is defined in the index.js

function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
maybe it’s getting munged away in the release build?

lilactown17:11:12

it could be some other error

lilactown17:11:26

like a collision once advanced compilation kicks in

eoliphant17:11:14

just tried forcing whitspace only

thheller17:11:25

what build target is this? :npm-module?

eoliphant17:11:00

it’s a pretty OOTB Fulcro w/ shadow config

thheller17:11:35

and how do you include the JS?

eoliphant17:11:14

it’s just an alias right now ["@grantsolutions/grant-solutions-ui" :as gsui]

thheller18:11:49

hmm not sure why this would work in dev but not release

eoliphant18:11:05

it’s weird. any problems I’ve seen before have been consistent. because of whatever lib weirdness

eoliphant18:11:09

they just didn’t work at all

eoliphant18:11:55

and not an expert, but the webpack and babel configs, seem mercifully simple

eoliphant18:11:20

module.exports = {
  entry: "./components",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
    library: "",
    libraryTarget: "commonjs"
  },
  plugins: [new MiniCssExtractPlugin(), new StyleLintPlugin()],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ["babel-loader", "eslint-loader"]
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "sass-loader"
        ]
      },
      {
        test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
        use: "file-loader"
      }
    ]
  },
  resolve: {
    extensions: [".js", ".jsx"]
  }
};
---

{
  "presets": ["@babel/env", "@babel/flow", "@babel/react"],
  "plugins": [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread",
    "flow-react-proptypes"
  ]
}

thheller18:11:44

that seems fine yes

thheller18:11:04

and if the file itself contains the require definition that should work fine

eoliphant18:11:45

this is ‘raw’ one

// @flow

// $FlowFixMe
import "../styles/main.scss";

export * from "./Button";
export * from "./Icon";
export * from "./Notification";
export * from "./Form";
export * from "./Modal";
export * from "./Popover";
export * from "./Search";
export * from "./List";
export * from "./Table";
export * from "./utils";
export * from "./Progress";

thheller18:11:18

doesn't matter

eoliphant18:11:20

and yeah, the one in dist.. defines __webpack_require__

eoliphant18:11:44

ok this is weird. when I speciffy whitespace

eoliphant18:11:47

it fails differntly

thheller18:11:22

don't specify whitespace

thheller18:11:30

its not a supported optimization mode for anything

eoliphant18:11:32

Uncaught TypeError: Cannot set property 'Error' of undefined
    at app.js:768
    at app.js:11615
And it looks like it’s goog.debug.Error that’s missing

eoliphant18:11:49

just tried it to see if it would help

eoliphant18:11:18

I think the docs may be out of date :optimizations supports :advanced, :simple or :whitespace

thheller18:11:11

can you send me the index.js?

eoliphant18:11:33

gotcha 🙂 ok, this is weird. I went to simple. same issue. But webpack.require__ is called before it’s defined

thheller18:11:36

I'm out of guesses

thheller18:11:20

JS doesn't work like CLJS so calling something that is defined later is fine

thheller18:11:31

as long as it is defined in the same scope

eoliphant18:11:23

just sent it

thheller18:11:08

thats a debug JS build

thheller18:11:22

that was not built with webpack in production mode

thheller18:11:31

you really really don't want to use that 😛

eoliphant18:11:59

lol ok, yeah this is from another group, i have the source so I can muck aroiund with it

eoliphant18:11:16

but beyond that it shuold still behave consistently right?

thheller18:11:18

I think it is webpack -p

thheller18:11:28

eval("__webpack_require__.r(__webpack_exports__);\n/* harm

thheller18:11:38

it just evals the code all over the place

thheller18:11:03

but since it evals a string clojure just probably thought that the __webpack__require function was unused and removed it

thheller18:11:21

if its built without debug mode it should be working fine

eoliphant18:11:30

will grab it and try building it locally

eoliphant18:11:50

ugh there’s not even a non-dev build

eoliphant18:11:55

in the scripts

thheller18:11:22

eek. nobody uses that for production builds then 😛

eoliphant18:11:28

"compile": "run-s compile:*",
    "compile:storybook": "build-storybook -o dist/storybook",
    "compile:webpack": "webpack --mode=development",
    "format": "run-s format:*",
    "format:icons": "eslint components/Icon/glyphs/*.jsx --fix",
    "prepack": "run-s clean:build test compile",

eoliphant18:11:50

yeah it’s still in dvelopment

eoliphant18:11:09

but they should be pushing the right build to the repo

thheller18:11:10

indeed. making everyone run the debug build is no fun 😉

thheller18:11:46

but FWIW using the build itself is already problematic

thheller18:11:08

it includes its own version of moment, react, react-dom, etc etc etc

thheller18:11:21

so they won't be compatible with the ones used by your CLJS

thheller18:11:48

or you'll have 2 versions loaded on the page

thheller18:11:17

so the entire setup doesn't seem ideal to me

eoliphant19:11:01

ok so given that I generally avoid JS like the plague

eoliphant19:11:17

what’s the best way to address that. make those dev dependencies?

thheller19:11:10

best way would be to just include the babel output and not package anything at all

thheller19:11:13

so no webpack

eoliphant19:11:30

ok this is another team. We’re ultimately their consumer, so I can make them do whatever I wish lol

eoliphant19:11:38

actually now that I think about it

eoliphant19:11:44

this shouldn’t be a super big deal

eoliphant19:11:54

they’re on the hook for the components

eoliphant19:11:02

and this demo app

thheller19:11:03

if you have the sources and the babel config already it should be easy

eoliphant19:11:46

yeah just figuring out the best, least disruptive way to tell them they have to do this lol

eoliphant19:11:14

they’re using storybook, etc etc. but to your point

eoliphant19:11:26

all that should still work just fine

thheller19:11:48

well storybook output probably should not be part of the npm package

thheller19:11:54

but yeah it doesn't hurt anyone if its there

eoliphant19:11:24

shoulda just foreced them to learn CLJS lol

eoliphant19:11:44

but in looking at their scripts

eoliphant19:11:16

they have all the usual jest, lint, etc stuff. That should be fine.. and separate ones to build and run storybook

eoliphant19:11:45

but ugh in looking at this, yeah storybook is all mixed in

thheller19:11:56

doesn't matter really

thheller19:11:59

just leave it there

thheller19:11:26

in the node_modules version of the lib you currently have

thheller19:11:36

run npm install

eoliphant19:11:00

So potentially, everything can remain the same

"start": "start-storybook --port 1999",
    "prepack": "run-s clean:build test compile",
    "clean": "yarn clean:build && yarn clean:modules",
    "clean:build": "rm -rf dist",
    "clean:modules": "rm -rf node_modules",
    "test": "run-s test:*",
    "test:flow": "flow",
    "test:eslint": "eslint components --ext .js --ext .jsx",
    "test:stylelint": "stylelint styles",
    "test:jest": "jest",
    "compile": "run-s compile:*",
    "compile:storybook": "build-storybook -o dist/storybook",
    "compile:webpack": "webpack --mode=development",
    "verify:eslint": "eslint --print-config .eslintrc | eslint-config-prettier-check",
    "format": "run-s format:*",
    "format:icons": "eslint components/Icon/glyphs/*.jsx --fix"
aside from the complie:webpack /

thheller19:11:26

then npx babel components --out-dir out

thheller19:11:29

just as a test

thheller19:11:42

yes all that can stay as is .. doesn't matter

eoliphant19:11:47

already pulled it down

thheller19:11:01

no do it in the version you have intalled

thheller19:11:04

not the source repo

thheller19:11:08

just a test

thheller19:11:09

the generated out folder should jsut contain a bunch of clean .js files

thheller19:11:54

["@grantsolutions/grant-solutions-ui/out/index.js" :as gsui] should then maybe work?

eoliphant19:11:26

ok so if that works

eoliphant19:11:35

then I can go yell at them lol

thheller19:11:29

you want to stay as close as possible to the original sources

thheller19:11:35

just need to get rid of .jsx really

thheller19:11:57

that avoid conflicts with already-bundled deps and stuff

thheller19:11:34

could clean up the package to get cleaner paths but thats next level stuff 😉

eoliphant19:11:10

got pulled into a call

eoliphant19:11:14

that did it 🙂

neural17:11:15

someone know what this error is???

neural17:11:55

'''1. Unhandled java.lang.IllegalStateException Can't change/establish root binding of: nrepl-cljs with set'''

neural17:11:16

i am in emacs !

neural18:11:09

@thheller you mean the var cider-jack-in-cljs-middlewares?

thheller18:11:32

how did you start the server instance?

neural18:11:15

i started a cider-jack-in then that repl i run the server/start! and shadow/watch.... then i tried the cider-connect-siblings-cljs

thheller18:11:35

ok so you are using lein or deps.edn to start the server?

thheller18:11:53

and which nrepl server are you connecting to?

thheller18:11:00

it does not appear to be the one started by shadow-cljs

neural18:11:12

maybe thats the problem... i started a regular cider-jack-in then when i started the shadow-cljs server inside that nrepl and it started a new repl!

thheller18:11:14

whatever nrepl server you are connected to must have the nrepl middleware loaded

thheller18:11:29

I don't know which one you are connected to or how to configure that one

neural18:11:40

my cider-jack-in-cljs-nrepl-middlewares is ("cider.piggieback/wrap-cljs-repl" "shadow.cljs.devtools.server.nrepl/cljs-load-file" "shadow.cljs.devtools.server.nrepl/cljs-eval" "shadow.cljs.devtools.server.nrepl/cljs-select")

neural18:11:11

shadow-cljs always starts a new nrepl????

thheller18:11:37

I have no idea what cider-jack-in-cljs-nrepl-middlewares is or means so I can't say if that is correct or not

thheller18:11:50

shadow-cljs starts its own nrepl server yes. you do not have to use that one if the nrepl server you are talking to has the middleware properly loaded

thheller18:11:10

maybe ask in #cider how to configure nrepl middleware for cider-jack-in + deps.edn

thheller18:11:20

I have no idea what that does

neural18:11:40

tks! will check that!

thheller18:11:51

you could just run shadow-cljs server and cider-connect or whatever thats called

thheller18:11:20

that ensures that everything is setup properly on the shadow-cljs side

neural18:11:35

ok.. but i need the oposite... i started a cider-connect with an already started server...

neural18:11:14

...then if i want shadow-cljs to run... i run embedeed in that server... but it always started its own server...

thheller18:11:16

its all about the middelware, you can connect to any nrepl server that has that middleware loaded

thheller18:11:44

it does NOT matter that its own server is started

thheller18:11:49

you do NOT need to use it

thheller18:11:01

it won't interfere with anything

thheller18:11:05

just don't talk to it

thheller18:11:16

it has nothing to do with your problem

neural18:11:49

but then how i started a shadow-cljs repl on that embeede server? its not the server from shadow-cljs

thheller18:11:06

this shows how to set everything up for lein. I just can't tell you how to do it for deps.edn + emacs

thheller18:11:13

I can't help you ... I have absolutely no clue how emacs does the project stuff

thheller18:11:34

you are starting an nrepl server somehow. that server does not have the middleware loaded.

thheller18:11:36

nothing else matters

neural18:11:56

tks Thomas!! im guessing its the middleware... gonna check that!!!